Skip to main content
Glama
hqn21

protocols-io-mcp-server

set_protocol_steps

Replace all steps in a protocols.io protocol by providing a new steps list with descriptions, materials, and references.

Instructions

Replace the entire steps list of a specific protocol by its protocol ID with a new steps list. The existing steps will be completely overwritten.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
protocol_idYesUnique identifier for the protocol
stepsYesList of steps to set for the protocol

Implementation Reference

  • The core handler function for the set_protocol_steps tool. Decorated with @mcp.tool() for registration. It deletes existing steps, creates new ones from the input list using ProtocolStepInput, links them sequentially, and returns the updated list of ProtocolStep objects.
    @mcp.tool()
    async def set_protocol_steps(
        protocol_id: Annotated[int, Field(description="Unique identifier for the protocol")],
        steps: Annotated[list[ProtocolStepInput], Field(description="List of steps to set for the protocol")]
    ) -> list[ProtocolStep] | ErrorMessage:
        """
        Replace the entire steps list of a specific protocol by its protocol ID with a new steps list. The existing steps will be completely overwritten.
        """
        if not steps:
            return ErrorMessage.from_string("At least one step is required to set the protocol steps.")
        # get all existing steps
        response_get_steps = await helpers.access_protocols_io_resource("GET", f"/v4/protocols/{protocol_id}/steps?content_format=markdown")
        if response_get_steps["status_code"] != 0:
            return ErrorMessage.from_string(response_get_steps['status_text'])
        step_existed = [ProtocolStep.from_api_response(step) for step in response_get_steps.get("payload", [])]
        # delete all existing steps
        response_delete_protocol_step = await helpers.access_protocols_io_resource("DELETE", f"/v4/protocols/{protocol_id}/steps", {"steps": [step.id for step in step_existed]})
        if response_delete_protocol_step["status_code"] != 0:
            return ErrorMessage.from_string(response_delete_protocol_step['status_text'])
        # set steps
        data = []
        previous_step_id = None
        for step in steps:
            step_content = await ProtocolStepInput.to_string(step)
            step_data = {
                "guid": uuid.uuid4().hex,
                "previous_guid": previous_step_id,
                "step": step_content
            }
            data.append(step_data)
            previous_step_id = step_data["guid"]
        response_set_steps = await helpers.access_protocols_io_resource("POST", f"/v4/protocols/{protocol_id}/steps", {"steps": data})
        if response_set_steps["status_code"] != 0:
            return ErrorMessage.from_string(response_set_steps['status_text'])
        # get updated steps
        response_get_steps = await helpers.access_protocols_io_resource("GET", f"/v4/protocols/{protocol_id}/steps?content_format=markdown")
        if response_get_steps["status_code"] != 0:
            return ErrorMessage.from_string(response_get_steps['status_text'])
        protocol_steps = [ProtocolStep.from_api_response(step) for step in response_get_steps.get("payload", [])]
        return protocol_steps
  • Pydantic model defining the input structure for protocol steps, used in the tool's 'steps' parameter. Includes methods for string conversion.
    class ProtocolStepInput(BaseModel):
        description: Annotated[str, Field(description="Description of the step (plain text only)")]
        materials: Annotated[list[Material], Field(description="Materials required for this step. Empty if no materials are needed")] = Field(default_factory=list)
        reference_protocol_ids: Annotated[list[int], Field(description="Protocol IDs referenced by this step. Empty if no references exist. Strongly recommend using at least one reference to ensure credibility")] = Field(default_factory=list)
    
        @staticmethod
        async def to_string(step: "ProtocolStepInput") -> str:
            step_content = f"{step.description}\n"
            if len(step.materials) + len(step.reference_protocol_ids) > 0:
                step_content += "\n"
            # add materials to step content
            if len(step.materials) > 0:
                step_content += "[Materials]\n"
                for material in step.materials:
                    step_content += f"- {material.name.replace(' ', '_')} {material.quantity} {material.unit.replace(' ', '_')}\n"
            # add reference protocols to step content
            if len(step.reference_protocol_ids) > 0:
                if len(step.materials) > 0:
                    step_content += "\n"
                step_content += "[Protocol References]\n"
                for protocol_id in step.reference_protocol_ids:
                    # get information about the referenced protocol
                    response_get_protocol = await helpers.access_protocols_io_resource("GET", f"/v4/protocols/{protocol_id}")
                    if response_get_protocol["status_code"] != 0:
                        return response_get_protocol["status_text"]
                    protocol = await Protocol.from_protocol_id(response_get_protocol["payload"]["id"])
                    step_content += f"- {protocol.title.replace('[', '<').replace(']', '>')}[{protocol.id}] {protocol.doi}\n"
            return step_content
  • Pydantic model for output ProtocolStep objects, parsed from API responses and used as return type (list thereof). Includes parsing logic from string format.
    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"]
            )

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hqn21/protocols-io-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server