Skip to main content
Glama
hqn21

protocols-io-mcp-server

add_protocol_step

Add a new step to an existing scientific protocol on protocols.io by providing the protocol ID and step details including description, materials, and references.

Instructions

Add a step to the end of the steps list for a specific protocol by its protocol ID.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
protocol_idYesUnique identifier for the protocol
stepYesStep to be added to the protocol

Implementation Reference

  • The handler function for the 'add_protocol_step' tool. It appends a new step to the protocol by linking it to the last existing step via previous_guid, formats the step content, posts to the API, and returns the updated steps list.
    @mcp.tool()
    async def add_protocol_step(
        protocol_id: Annotated[int, Field(description="Unique identifier for the protocol")],
        step: Annotated[ProtocolStepInput, Field(description="Step to be added to the protocol")]
    ) -> list[ProtocolStep] | ErrorMessage:
        """
        Add a step to the end of the steps list for a specific protocol by its protocol ID.
        """
        # 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", [])]
        # get last step ID
        previous_step_id = step_existed[-1].id if step_existed else None
        # add step
        step_content = await ProtocolStepInput.to_string(step)
        step_data = {
            "guid": uuid.uuid4().hex,
            "previous_guid": previous_step_id,
            "step": step_content
        }
        response_add_step = await helpers.access_protocols_io_resource("POST", f"/v4/protocols/{protocol_id}/steps", {"steps": [step_data]})
        if response_add_step["status_code"] != 0:
            return ErrorMessage.from_string(response_add_step["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 the step: description, list of materials (each with name, quantity, unit), and list of reference protocol IDs. Includes a to_string method to format the step content for API submission, embedding material lists and fetching details for referenced protocols.
    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 individual material used in protocol steps, with name, quantity (non-negative float), and unit.
    class Material(BaseModel):
        name: Annotated[str, Field(description="Name of the material")]
        quantity: Annotated[float, Field(description="Amount of material needed", ge=0.0)]
        unit: Annotated[str, Field(description="Unit of measurement for the material, e.g., 'mL', 'g', 'μL'")]

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