get_protocol_steps
Retrieve detailed steps for scientific protocols by providing the protocol ID, enabling access to structured methodology information from protocols.io.
Instructions
Retrieve the steps for a specific protocol by its protocol ID.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| protocol_id | Yes | Unique identifier for the protocol |
Implementation Reference
- The handler function for the 'get_protocol_steps' tool. It takes a protocol_id, calls the protocols.io API to fetch steps, parses them into ProtocolStep objects, or returns an error.@mcp.tool() async def get_protocol_steps( protocol_id: Annotated[int, Field(description="Unique identifier for the protocol")] ) -> list[ProtocolStep] | ErrorMessage: """ Retrieve the steps for a specific protocol by its protocol ID. """ response = await helpers.access_protocols_io_resource("GET", f"/v4/protocols/{protocol_id}/steps?content_format=markdown") if response["status_code"] != 0: return ErrorMessage.from_string(response["status_text"]) steps = [ProtocolStep.from_api_response(step) for step in response.get("payload", [])] return steps
- Pydantic model for ProtocolStep, defining the output structure for protocol steps, including parsing logic from API response.class ProtocolStep(BaseModel): id: Annotated[str, Field(description="Unique identifier for the step")] description: Annotated[str, Field(description="Description of the step")] materials: Annotated[list[Material], Field(description="Materials required for this step. Empty if no materials are needed or if source data could not be parsed")] = Field(default_factory=list) reference_protocol_ids: Annotated[list[int], Field(description="Protocol IDs referenced by this step. Empty if no references exist or if source data could not be parsed")] = Field(default_factory=list) @staticmethod def parse(step: str) -> dict: description = [] materials = [] reference_protocol_ids = [] material_flag = False reference_flag = False for line in step.splitlines(): if material_flag is False and reference_flag is False: if line == "[Materials]": material_flag = True reference_flag = False elif line == "[Protocol References]": reference_flag = True material_flag = False elif len(line) > 0: description.append(line) elif material_flag: if len(line) == 0 or line[0] != '-': material_flag = False continue data = line.split() materials.append(Material( name=data[1], quantity=float(data[2]), unit=data[3] )) elif reference_flag: if len(line) == 0 or line[0] != '-': reference_flag = False continue data = line.split("[")[1].split("]")[0] reference_protocol_ids.append(int(data)) description = "\n".join(description) return { "description": description, "materials": materials, "reference_protocol_ids": reference_protocol_ids } @classmethod def from_api_response(cls, data: dict) -> "ProtocolStep": parsed_step = ProtocolStep.parse(data["step"]) return cls( id=data["guid"], description=parsed_step["description"], materials=parsed_step["materials"], reference_protocol_ids=parsed_step["reference_protocol_ids"] )
- src/protocols_io_mcp/server.py:10-10 (registration)Registration of tools by importing the tools module, which executes the @mcp.tool() decorators in protocol.py to register 'get_protocol_steps' and other tools.importlib.import_module('protocols_io_mcp.tools')
- Helper function used by the tool to make authenticated HTTP requests to the protocols.io API.async def access_protocols_io_resource(method: Literal["GET", "POST", "PUT", "DELETE"], path: str, data: dict = None) -> dict[str, Any]: """Access protocols.io API with specified method and path.""" headers = { "Authorization": f"Bearer {PROTOCOLS_IO_CLIENT_ACCESS_TOKEN}" } async with httpx.AsyncClient(timeout=30.0) as client: response = await client.request(method, f"{PROTOCOLS_IO_API_URL}{path}", json=data, headers=headers) return response.json()