Skip to main content
Glama
claudiomedina17

CardioTriage MCP Server

generate_structured_handoff_note

Create deterministic SBAR-style handoff notes for synthetic cardiovascular cases in English, Spanish, or Portuguese after risk and missing-data review.

Instructions

Generate a deterministic SBAR-style handoff note for a synthetic case in English, Spanish, or Portuguese. Use after risk and missing-data review. NOT for clinical use.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
case_idYes
languageNoen

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
case_idYes
languageNoen
styleNoSBAR
noteYes
textYes
disclaimerNoNOT for clinical use. Synthetic demo data only. Clinician validation required.

Implementation Reference

  • Core implementation of the generate_structured_handoff_note tool. Fetches synthetic case data, summarizes risk, identifies missing critical data, builds a HandoffNote with situation/background/assessment/recommendation/safety, and renders it as SBAR text. Returns HandoffNoteOutput.
    def generate_structured_handoff_note(case_id: str, language: Language = "en") -> HandoffNoteOutput:
        case = get_synthetic_case(case_id)
        risk = summarize_cardiovascular_risk_signals(case.case_id)
        missing = analyze_missing_critical_data(case.case_id)
        recommendations = _recommendations(case.case_id)
    
        note = HandoffNote(
            situation=f"Synthetic {case.case_id}: {case.presenting_problem}.",
            background=(
                f"{case.age}-year-old {case.sex}; vitals: {_format_dict(case.vitals)}; "
                f"labs: {_format_dict(case.labs) if case.labs else 'none documented'}; ECG: {case.ecg}."
            ),
            assessment=_assessment(case.case_id, risk),
            recommendation=recommendations,
            safety=_safety(language),
        )
        text = _render_sbar(note, missing_fields=[item.field for item in missing.missing], language=language)
        return HandoffNoteOutput(case_id=case.case_id, language=language, note=note, text=text)
  • Pydantic models for HandoffNote (situation, background, assessment, recommendation, safety) and HandoffNoteOutput (case_id, language, style='SBAR', note, text, disclaimer). Also defines Language type literal (en/es/pt).
    class HandoffNote(BaseModel):
        situation: str
        background: str
        assessment: list[str]
        recommendation: list[str]
        safety: str
    
    
    class HandoffNoteOutput(BaseModel):
        case_id: str
        language: Language = "en"
        style: Literal["SBAR"] = "SBAR"
        note: HandoffNote
        text: str
        disclaimer: str = DISCLAIMER
  • server/main.py:24-31 (registration)
    Tool name 'generate_structured_handoff_note' listed in REGISTERED_TOOL_NAMES constant.
    REGISTERED_TOOL_NAMES = [
        "list_synthetic_cases",
        "get_fhir_case_bundle",
        "analyze_missing_critical_data",
        "summarize_cardiovascular_risk_signals",
        "generate_structured_handoff_note",
        "interpret_12_lead_ecg",
    ]
  • server/main.py:144-147 (registration)
    MCP server wrapper function that delegates to handoff_note_tool (the implementation from server/tools/handoff_note.py). Has docstring used as tool description.
    def generate_structured_handoff_note(case_id: str, language: Language = "en") -> HandoffNoteOutput:
        """Generate a deterministic SBAR-style handoff note for a synthetic case in English, Spanish, or Portuguese. Use after risk and missing-data review. NOT for clinical use."""
    
        return handoff_note_tool(case_id, language=language)
  • server/main.py:160-166 (registration)
    Tool registered via server.tool()(generate_structured_handoff_note) in register_tools().
    def register_tools(server: FastMCP) -> None:
        server.tool()(list_synthetic_cases)
        server.tool()(get_fhir_case_bundle)
        server.tool()(analyze_missing_critical_data)
        server.tool()(summarize_cardiovascular_risk_signals)
        server.tool()(generate_structured_handoff_note)
        server.tool()(interpret_12_lead_ecg)
  • _render_sbar helper: formats the SBAR text using localized labels and includes missing critical data. Used by the main handler.
    def _render_sbar(note: HandoffNote, missing_fields: list[str], language: Language) -> str:
        situation, background, assessment, recommendation, safety = _labels(language)
        missing_text = _missing_text(missing_fields, language)
        assessment_text = " ".join(note.assessment)
        recommendation_text = " ".join(note.recommendation)
        missing_label = {
            "en": "Missing critical data",
            "es": "Datos criticos faltantes",
            "pt": "Dados criticos ausentes",
        }[language]
        return (
            f"{situation}: {note.situation}\n"
            f"{background}: {note.background}\n"
            f"{assessment}: {assessment_text} {missing_label}: {missing_text}.\n"
            f"{recommendation}: {recommendation_text}\n"
            f"{safety}: {note.safety}"
        )
  • _recommendations helper: returns case-specific recommendation lists (A, B, D, or default).
    def _recommendations(case_id: str) -> list[str]:
        if case_id == "A":
            return [
                "Immediate clinician review for high-risk ACS/STEMI workflow.",
                "Repeat ECG and follow serial troponin availability.",
                "Document contraindications before antithrombotic or reperfusion decisions.",
            ]
        if case_id == "B":
            return [
                "Evaluate heart failure and ACS differential with available local diagnostics.",
                "Prioritize BNP/NT-proBNP, troponin, echocardiography, and Chagas serology if appropriate.",
                "Escalate if oxygenation, blood pressure, or work of breathing worsens.",
            ]
        if case_id == "D":
            return [
                "Request echocardiography to characterize LVEF, chamber size, and valvular disease.",
                "Obtain troponin and BNP/NT-proBNP if clinically indicated for dyspnea workup.",
                "Arrange outpatient cardiology follow-up and escalate if oxygenation, blood pressure, or symptoms worsen.",
            ]
        return [
            "Clinician review for low-risk chest-pain disposition.",
            "Use return precautions and follow-up planning if discharge is selected.",
            "Reassess if symptoms recur, evolve, or red flags appear.",
        ]
  • _assessment helper: returns case-specific assessment strings, with special handling for case D.
    def _assessment(case_id: str, risk) -> list[str]:
        if case_id == "D":
            return [
                (
                    "MEDIUM: LBBB + low-voltage structural cardiomyopathy pattern - "
                    "uncalibrated ECG demo score supports structural cardiomyopathy workup, "
                    "not autonomous etiology diagnosis."
                )
            ]
        return [
            f"{signal.level.upper()}: {signal.label} - {signal.rationale}"
            for signal in risk.signals
        ]
  • _safety helper: returns localized safety disclaimer in en/es/pt.
    def _safety(language: Language) -> str:
        if language == "es":
            return (
                "NO para uso clinico. Solo datos demo sinteticos / publicos de-identificados. "
                "Un clinico licenciado debe validar todos los hallazgos y acciones."
            )
        if language == "pt":
            return (
                "NAO para uso clinico. Apenas dados demo sinteticos / publicos de-identificados. "
                "Um clinico licenciado deve validar todos os achados e acoes."
            )
        return (
            "NOT for clinical use. Synthetic / public de-identified demo data only. "
            "A licensed clinician must validate all findings and actions."
        )
  • _labels helper: returns localized SBAR section labels for en/es/pt.
    def _labels(language: Language) -> tuple[str, str, str, str, str]:
        if language == "es":
            return ("Situacion", "Antecedentes", "Evaluacion", "Recomendacion", "Seguridad")
        if language == "pt":
            return ("Situacao", "Contexto", "Avaliacao", "Recomendacao", "Seguranca")
        return ("Situation", "Background", "Assessment", "Recommendation", "Safety")
  • _missing_text helper: formats missing fields text localized by language.
    def _missing_text(missing_fields: list[str], language: Language) -> str:
        if missing_fields:
            return ", ".join(missing_fields)
        if language == "es":
            return "ninguno critico en el caso demo"
        if language == "pt":
            return "nenhum critico no caso demo"
        return "none critical in synthetic case"
  • _format_dict helper: formats a dict as 'key: value; key: value' string.
    def _format_dict(values: dict[str, str]) -> str:
        return "; ".join(f"{key}: {value}" for key, value in values.items())
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses key behavioral traits: deterministic output, SBAR-style format, synthetic case handling, and language support. The 'NOT for clinical use' warning adds important safety context. With no annotations provided, the description fully carries the transparency burden.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, each essential. Front-loaded with the core action and constraints. No unnecessary text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (two parameters, output schema present), the description covers purpose, usage timing, behavioral traits, and language options. It is complete enough for an AI agent to decide when and how to invoke it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate. The description mentions language options (English, Spanish, Portuguese), which maps to the language parameter enum, but does not explain the case_id parameter. This is partial value—adequate for a two-parameter tool, but not fully explanatory.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool generates a deterministic SBAR-style handoff note for synthetic cases, with specific language options. It differentiates itself from sibling tools by mentioning a generative note-making purpose after review.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states 'Use after risk and missing-data review,' providing clear context for when to invoke the tool, and also warns 'NOT for clinical use.' Although it doesn't name alternative tools, the guidance is sufficient for appropriate usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/claudiomedina17/cardiotriage-mcp'

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