"""
Troi MCP Server
================
Connects Claude Desktop to Troi project management & time tracking.

Setup:
  1. pip3 install mcp requests
  2. Add to Claude Desktop config (see README.md)
  3. Set TROI_URL, TROI_USER, TROI_API_KEY, TROI_CLIENT_ID in env
"""

import os
import json
import asyncio
from typing import Any

import mcp.server.stdio
import mcp.types as types
from mcp.server import Server

from .troi import TroiClient, TroiError

# ---------------------------------------------------------------------------
# Init
# ---------------------------------------------------------------------------

server = Server("troi")
client: TroiClient | None = None


def _init_client() -> TroiClient:
    global client
    if client is None:
        client = TroiClient()
    return client


def _ok(data: Any) -> list[types.TextContent]:
    """Format a successful result."""
    if isinstance(data, (dict, list)):
        text = json.dumps(data, indent=2, ensure_ascii=False, default=str)
    else:
        text = str(data)
    return [types.TextContent(type="text", text=text)]


def _err(e: Exception) -> list[types.TextContent]:
    """Format an error result."""
    if isinstance(e, TroiError):
        msg = f"Troi API Error [{e.status_code}]: {e}\nURL: {e.url}"
        if e.body:
            msg += f"\nResponse: {e.body}"
    else:
        msg = f"Error: {e}"
    return [types.TextContent(type="text", text=msg)]


# ---------------------------------------------------------------------------
# Permission layer — server-enforced confirmation for write operations
# ---------------------------------------------------------------------------

# "write"     = standard create/update/delete, requires confirmed=true
# "dangerous" = irreversible actions, requires confirmed=true + extra warning
# (tools not listed here are read-only, no gate needed)
WRITE_TOOLS: dict[str, str] = {
    # CREATE
    "troi_create_project":                 "write",
    "troi_create_subproject":              "write",
    "troi_create_position":                "write",
    "troi_create_position_from_pricelist": "write",
    "troi_create_time_entry":              "write",
    "troi_create_customer":                "write",
    "troi_create_customer_with_contact":   "write",
    "troi_create_contact":                 "write",
    "troi_create_offer":                   "write",
    # UPDATE
    "troi_update_project":                 "write",
    "troi_update_subproject":              "write",
    "troi_update_position":                "write",
    "troi_update_time_entry":              "write",
    "troi_update_customer":                "write",
    "troi_update_contact":                 "write",
    # DELETE
    "troi_delete_project":                 "write",
    "troi_delete_subproject":              "write",
    "troi_delete_position":                "write",
    "troi_delete_time_entry":              "write",
    # DANGEROUS (irreversible)
    "troi_generate_offer_number":          "dangerous",
    "troi_start_approval":                 "dangerous",
}


def _action_label(tool_name: str) -> str:
    """Human-readable action label from tool name."""
    parts = tool_name.replace("troi_", "").split("_", 1)
    verb = parts[0].upper()
    noun = parts[1].replace("_", " ") if len(parts) > 1 else ""
    return f"{verb} {noun}"


def _build_preview(tool_name: str, arguments: dict) -> str:
    """Build a human-readable preview for a write operation.

    Returned instead of executing when ``confirmed`` is not set.
    """
    tier = WRITE_TOOLS.get(tool_name, "write")
    header = "⚠️  IRREVERSIBLE OPERATION" if tier == "dangerous" else "PENDING OPERATION"

    lines = [f"=== {header} — PREVIEW ==="]
    lines.append(f"Tool: {tool_name}")
    lines.append(f"Action: {_action_label(tool_name)}")
    lines.append("")

    # Format arguments (excluding 'confirmed' itself)
    display_args = {k: v for k, v in arguments.items() if k != "confirmed"}
    for key, value in display_args.items():
        if isinstance(value, dict):
            lines.append(f"  {key}:")
            for k2, v2 in value.items():
                lines.append(f"    {k2}: {v2}")
        elif isinstance(value, list):
            lines.append(f"  {key}: [{len(value)} items]")
            for i, item in enumerate(value[:5]):
                if isinstance(item, dict):
                    lines.append(f"    [{i}] {json.dumps(item, ensure_ascii=False)}")
                else:
                    lines.append(f"    [{i}] {item}")
            if len(value) > 5:
                lines.append(f"    ... and {len(value) - 5} more")
        else:
            lines.append(f"  {key}: {value}")

    lines.append("")
    if tier == "dangerous":
        lines.append("⚠️  WARNING: This action CANNOT be undone.")
    lines.append("To execute, re-call this tool with confirmed=true.")
    lines.append("===========================")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Tool definitions
# ---------------------------------------------------------------------------

@server.list_tools()
async def list_tools() -> list[types.Tool]:
    tools = [

        # ── PROJECTS ────────────────────────────────────────────────
        types.Tool(
            name="troi_list_projects",
            description=(
                "List projects from Troi. Use to get an overview of active projects, "
                "search by name, or filter by customer, status, leader. "
                "Returns project IDs, names, customers, and status."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "search": {"type": "string", "description": "Search projects by name"},
                    "customer_id": {"type": "integer", "description": "Filter by Troi customer ID"},
                    "in_process": {"type": "boolean", "description": "True=active, False=archived, omit=all"},
                    "project_status_id": {"type": "integer", "description": "Filter by project status ID"},
                    "project_leader_id": {"type": "integer", "description": "Filter by project leader employee ID"},
                    "size": {"type": "integer", "description": "Max results (default 200)", "default": 200},
                },
            },
        ),

        types.Tool(
            name="troi_get_project",
            description="Get full details of a single Troi project by its ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "The project ID"},
                },
                "required": ["project_id"],
            },
        ),

        types.Tool(
            name="troi_create_project",
            description=(
                "Create a new project in Troi.\n\n"
                "Defaults to project_type=28 (Custom Projekt) and status=55 (angeboten) "
                "if not specified. This ensures projects are visible in the Troi UI.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute.\n\n"
                "Use troi_list_customers to get customer IDs, troi_list_project_types for type IDs, "
                "and troi_list_employees for leader IDs."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "name": {"type": "string", "description": "Project name (required)"},
                    "customer": {"type": "string", "description": "Customer ID (hash string from troi_list_customers)"},
                    "project_leader": {"type": "integer", "description": "Employee ID of project leader"},
                    "project_type": {"type": "string", "description": "Project type ID (default: 28 = Custom Projekt)"},
                    "status_id": {"type": "integer", "description": "Project status ID (default: 55 = angeboten)", "default": 55},
                    "reporting_date": {"type": "string", "description": "Reporting/invoice date YYYY-MM-DD (default: today)"},
                    "start_date": {"type": "string", "description": "Project start date YYYY-MM-DD (default: today)"},
                    "end_date": {"type": "string", "description": "Project end date YYYY-MM-DD (default: +30 days)"},
                    "external_description": {"type": "string", "description": "Client-facing project description (shown on offer/quote)"},
                    "internal_description": {"type": "string", "description": "Internal project notes (not shown to client)"},
                },
                "required": ["name", "customer"],
            },
        ),

        types.Tool(
            name="troi_update_project",
            description="Update an existing project.\n\nServer-enforced: a preview is returned first. Re-call with confirmed=true to execute.",
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "The project ID to update"},
                    "data": {"type": "object", "description": "Fields to update"},
                },
                "required": ["project_id", "data"],
            },
        ),

        types.Tool(
            name="troi_delete_project",
            description="Delete a project.\n\nServer-enforced: a preview is returned first. Re-call with confirmed=true to execute.",
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "The project ID to delete"},
                },
                "required": ["project_id"],
            },
        ),

        # ── SUBPROJECTS ────────────────────────────────────────────
        types.Tool(
            name="troi_list_subprojects",
            description="List subprojects (project sections/groups). Filter by project ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "Filter by project ID"},
                },
            },
        ),

        types.Tool(
            name="troi_get_subproject",
            description="Get details of a single subproject by ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "subproject_id": {"type": "integer", "description": "The subproject ID"},
                },
                "required": ["subproject_id"],
            },
        ),

        types.Tool(
            name="troi_create_subproject",
            description=(
                "Create a subproject (section/group) under a project.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute.\n\n"
                "Use troi_list_project_types to find valid project type IDs."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "Parent project ID (required)"},
                    "name": {"type": "string", "description": "Subproject name (required)"},
                    "project_type_id": {"type": "integer", "description": "Project type ID (default: 28)", "default": 28},
                },
                "required": ["project_id", "name"],
            },
        ),

        types.Tool(
            name="troi_update_subproject",
            description="Update an existing subproject.\n\nServer-enforced: a preview is returned first. Re-call with confirmed=true to execute.",
            inputSchema={
                "type": "object",
                "properties": {
                    "subproject_id": {"type": "integer", "description": "The subproject ID to update"},
                    "data": {"type": "object", "description": "Fields to update"},
                },
                "required": ["subproject_id", "data"],
            },
        ),

        types.Tool(
            name="troi_delete_subproject",
            description="Delete a subproject.\n\nServer-enforced: a preview is returned first. Re-call with confirmed=true to execute.",
            inputSchema={
                "type": "object",
                "properties": {
                    "subproject_id": {"type": "integer", "description": "The subproject ID to delete"},
                    "project_id": {"type": "integer", "description": "Parent project ID (optional, auto-detected if not given)"},
                },
                "required": ["subproject_id"],
            },
        ),

        # ── CALCULATION POSITIONS ───────────────────────────────────
        types.Tool(
            name="troi_list_positions",
            description=(
                "List calculation positions (service lines / budget items) for a project. "
                "Shows what services are booked under a project with budget and hours."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "Filter by project ID"},
                    "search": {"type": "string", "description": "Search positions by name"},
                    "favorites_only": {"type": "boolean", "description": "Only show favorites", "default": False},
                },
            },
        ),

        types.Tool(
            name="troi_get_position",
            description="Get details of a single calculation position by ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "position_id": {"type": "integer", "description": "The position ID"},
                },
                "required": ["position_id"],
            },
        ),

        types.Tool(
            name="troi_create_position",
            description=(
                "Create a calculation position (offer line item) in a project. "
                "This is how you build offers: each position is a line item with name, quantity, and price.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "description": (
                            "Position data. Required: "
                            "Name (string), "
                            "Project ({\"Path\": \"/projects/{id}\"}), "
                            "IsExternalService (bool), IsBillable (bool). "
                            "For offers also include: "
                            "Quantity (int), SalePrice (float), "
                            "Unit ({\"Path\": \"/misc/units/{id}\"}), "
                            "Subproject ({\"Path\": \"/subprojects/{id}\"}), "
                            "IsPrintable (bool, true to include in offer), "
                            "IsOptional (bool), "
                            "ExternalDescription (client-facing text), "
                            "InternalDescription (internal notes)."
                        ),
                    },
                },
                "required": ["data"],
            },
        ),

        types.Tool(
            name="troi_update_position",
            description=(
                "Update a calculation position (offer line item).\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "position_id": {"type": "integer", "description": "The position ID to update"},
                    "data": {"type": "object", "description": "Fields to update (e.g. Name, SalePrice, Quantity)"},
                },
                "required": ["position_id", "data"],
            },
        ),

        types.Tool(
            name="troi_delete_position",
            description="Delete a calculation position.\n\nServer-enforced: a preview is returned first. Re-call with confirmed=true to execute.",
            inputSchema={
                "type": "object",
                "properties": {
                    "position_id": {"type": "integer", "description": "The position ID to delete"},
                },
                "required": ["position_id"],
            },
        ),

        # ── PRICE LIST ──────────────────────────────────────────────
        types.Tool(
            name="troi_list_pricelist_groups",
            description=(
                "List available price list groups (Preislisten) in Troi. "
                "Returns group UUIDs and names. Use the UUID to browse items in a specific price list."
            ),
            inputSchema={"type": "object", "properties": {}},
        ),

        types.Tool(
            name="troi_list_pricelist_items",
            description=(
                "List all items in a price list group. Returns item UUIDs, names, prices, "
                "units, and descriptions. Use these UUIDs with troi_create_position_from_pricelist "
                "to add items as calculation positions to a project."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "group_id": {
                        "type": "string",
                        "description": "Price list group UUID (from troi_list_pricelist_groups). If omitted, uses the first active group.",
                    },
                },
            },
        ),

        types.Tool(
            name="troi_create_position_from_pricelist",
            description=(
                "Create a calculation position (offer line item) by adding a price list item "
                "to a project/subproject. This is the reliable way to create positions.\n\n"
                "Workflow: 1) troi_list_pricelist_items to find the item UUID, "
                "2) this tool to add it to a project.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "Target project ID (required)"},
                    "subproject_id": {"type": "integer", "description": "Target subproject ID (required)"},
                    "item_uuid": {"type": "string", "description": "UUID of the price list item to add (required)"},
                    "quantity": {"type": "number", "description": "Quantity to set (default 1)", "default": 1},
                    "sale_price_override": {"type": "number", "description": "Override the price list sale price per unit (optional)"},
                    "group_id": {"type": "string", "description": "Price list group UUID (auto-detected if omitted)"},
                },
                "required": ["project_id", "subproject_id", "item_uuid"],
            },
        ),

        # ── UNITS & REFERENCE DATA ────────────────────────────────
        types.Tool(
            name="troi_list_units",
            description="List available units in Troi (e.g. hours, pieces, days). Use when creating positions to find the right unit ID.",
            inputSchema={"type": "object", "properties": {}},
        ),

        types.Tool(
            name="troi_list_project_types",
            description="List available project types in Troi. Use when creating a project to find valid type IDs.",
            inputSchema={"type": "object", "properties": {}},
        ),

        types.Tool(
            name="troi_list_project_statuses",
            description="List available project statuses in Troi.",
            inputSchema={"type": "object", "properties": {}},
        ),

        # ── TIME ENTRIES ────────────────────────────────────────────
        types.Tool(
            name="troi_list_time_entries",
            description=(
                "List time entries (booked hours) for a project or position. "
                "Can filter by date range. Returns employee, hours, date, and description."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "Filter by project ID"},
                    "calculation_position_id": {"type": "integer", "description": "Filter by position ID"},
                    "start_date": {"type": "string", "description": "Start date YYYY-MM-DD"},
                    "end_date": {"type": "string", "description": "End date YYYY-MM-DD"},
                },
            },
        ),

        types.Tool(
            name="troi_get_time_entry",
            description="Get details of a single time entry by ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "entry_id": {"type": "integer", "description": "The time entry ID"},
                },
                "required": ["entry_id"],
            },
        ),

        types.Tool(
            name="troi_create_time_entry",
            description=(
                "Create a new time entry in Troi. Requires at least a calculation position ID, "
                "date, and hours/quantity.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "description": (
                            "Time entry data. Must include at minimum: "
                            "calculationPositionId (int), date (YYYY-MM-DD), "
                            "hours or quantity (float). Optional: description (string)."
                        ),
                    },
                },
                "required": ["data"],
            },
        ),

        types.Tool(
            name="troi_update_time_entry",
            description=(
                "Update an existing time entry.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "entry_id": {"type": "integer", "description": "The time entry ID to update"},
                    "data": {"type": "object", "description": "Fields to update"},
                },
                "required": ["entry_id", "data"],
            },
        ),

        types.Tool(
            name="troi_delete_time_entry",
            description=(
                "Delete a time entry.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "entry_id": {"type": "integer", "description": "The time entry ID to delete"},
                },
                "required": ["entry_id"],
            },
        ),

        # ── EMPLOYEES ───────────────────────────────────────────────
        types.Tool(
            name="troi_list_employees",
            description="List all employees in the Troi workspace.",
            inputSchema={
                "type": "object",
                "properties": {
                    "show_inactive": {"type": "boolean", "description": "Include inactive employees", "default": False},
                },
            },
        ),

        types.Tool(
            name="troi_get_employee",
            description="Get details of a single employee by ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "employee_id": {"type": "integer", "description": "The employee ID"},
                },
                "required": ["employee_id"],
            },
        ),

        # ── CUSTOMERS ───────────────────────────────────────────────
        types.Tool(
            name="troi_list_customers",
            description="List customers (Auftraggeber) in Troi.",
            inputSchema={
                "type": "object",
                "properties": {
                    "is_active": {"type": "boolean", "description": "Filter: true=active, false=inactive, omit=all"},
                },
            },
        ),

        types.Tool(
            name="troi_get_customer",
            description="Get details of a single customer by ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "customer_id": {"type": "integer", "description": "The customer ID"},
                },
                "required": ["customer_id"],
            },
        ),

        types.Tool(
            name="troi_create_customer",
            description=(
                "Create a customer (Auftraggeber) in Troi linked to an existing contact (company or department). "
                "The shortcut (3 uppercase letters) is used as prefix for project numbers. "
                "Defaults to 14-day payment terms (Netto-Tage) and active status.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute.\n\n"
                "Use troi_list_contacts or troi_create_contact to get the contact_id first."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "name": {"type": "string", "description": "Customer name (usually = company or department name)"},
                    "contact_id": {"type": "integer", "description": "Contact:Company ID to link (company or department)"},
                    "shortcut": {"type": "string", "description": "3-letter uppercase abbreviation (e.g. 'MER'). Used as project number prefix."},
                    "is_active": {"type": "boolean", "description": "Whether customer is active (default: true)", "default": True},
                    "payment_term_id": {"type": "integer", "description": "Payment term ID"},
                    "vat_number": {"type": "string", "description": "VAT ID (USt-ID)"},
                    "tax_number": {"type": "string", "description": "Tax number (Steuernummer)"},
                },
                "required": ["name", "contact_id", "shortcut"],
            },
        ),

        types.Tool(
            name="troi_update_customer",
            description=(
                "Update an existing customer (Auftraggeber). Only specified fields are changed.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "customer_id": {"type": "string", "description": "Customer hash ID (MD5 string from troi_list_customers or troi_create_customer)"},
                    "data": {
                        "type": "object",
                        "description": "Fields to update. Supported: name, shortcut, is_active, contact_id, net_days, payment_term_id, vat_number, tax_number.",
                    },
                },
                "required": ["customer_id", "data"],
            },
        ),

        types.Tool(
            name="troi_create_customer_with_contact",
            description=(
                "Create a customer with full contact hierarchy in one call. "
                "Supports both simple (company→person→customer) and corporate "
                "(company→department→person→customer) structures.\n\n"
                "Includes duplicate checking: searches for existing companies by name, "
                "and checks for existing departments under the company.\n\n"
                "Workflow:\n"
                "1. Find or create company contact\n"
                "2. Optionally create department under company\n"
                "3. Optionally create contact person\n"
                "4. Create customer linked to company or department\n\n"
                "Requires:\n"
                "- company_name and shortcut are always required\n"
                "- Set department_name for corporate variant (company must exist)\n"
                "- Set contact_last_name to create a contact person\n\n"
                "Returns customer_id (hash), all created contact IDs, and flags.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "company_name": {"type": "string", "description": "Company name to find or create"},
                    "shortcut": {"type": "string", "description": "3-letter customer abbreviation (e.g. 'MER')"},
                    "company_id": {"type": "integer", "description": "If known, skip company search and use this ID"},
                    "create_company_if_missing": {"type": "boolean", "description": "Create company if not found (default: true)", "default": True},
                    # Department (optional — corporate variant)
                    "department_name": {"type": "string", "description": "Department/branch name (triggers corporate variant)"},
                    "department_street": {"type": "string", "description": "Department street address"},
                    "department_zip_code": {"type": "string", "description": "Department ZIP code"},
                    "department_city": {"type": "string", "description": "Department city"},
                    "department_country_iso": {"type": "string", "description": "Department country ISO (default: DE)"},
                    # Company address & vCard (for new companies)
                    "street": {"type": "string", "description": "Company street address"},
                    "zip_code": {"type": "string", "description": "Company ZIP code"},
                    "city": {"type": "string", "description": "Company city"},
                    "country_iso": {"type": "string", "description": "Company country ISO (default: DE)"},
                    "company_email": {"type": "string", "description": "Company email (vCard Kontaktdaten)"},
                    "company_phone": {"type": "string", "description": "Company phone (vCard Kontaktdaten)"},
                    "company_website": {"type": "string", "description": "Company website (vCard Kontaktdaten)"},
                    # Customer
                    "is_active": {"type": "boolean", "description": "Customer active flag (default: true)", "default": True},
                    "payment_term_id": {"type": "integer", "description": "Payment term ID"},
                    # Contact person (optional)
                    "contact_first_name": {"type": "string", "description": "Contact person first name"},
                    "contact_last_name": {"type": "string", "description": "Contact person last name (triggers person creation)"},
                    "contact_email": {"type": "string", "description": "Contact person email"},
                    "contact_phone": {"type": "string", "description": "Contact person phone"},
                    "contact_mobile": {"type": "string", "description": "Contact person mobile"},
                    "contact_position": {"type": "string", "description": "Contact person job title"},
                    "contact_salutation": {"type": "string", "description": "Contact person salutation ('Herr'/'Frau')"},
                },
                "required": ["company_name", "shortcut"],
            },
        ),

        # ── CLIENTS ─────────────────────────────────────────────────
        types.Tool(
            name="troi_list_clients",
            description="List all clients (Mandanten) in the Troi instance.",
            inputSchema={"type": "object", "properties": {}},
        ),

        types.Tool(
            name="troi_get_client",
            description="Get details of a single client by ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "client_id": {"type": "integer", "description": "The client ID"},
                },
                "required": ["client_id"],
            },
        ),

        # ── ABSENCES ────────────────────────────────────────────────
        types.Tool(
            name="troi_list_absences",
            description=(
                "List absences (Urlaub, Krankheit, etc.) from Troi. "
                "Can filter by date range and employee."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "start": {"type": "string", "description": "Start date YYYY-MM-DD"},
                    "end": {"type": "string", "description": "End date YYYY-MM-DD"},
                    "employee_id": {"type": "integer", "description": "Filter by employee ID"},
                },
            },
        ),

        types.Tool(
            name="troi_get_team_absences",
            description=(
                "Get a consolidated view of absences for ALL active employees in a date range. "
                "Returns each employee's absences (Urlaub, Krank, Zeitkontoausgleich) with totals. "
                "Defaults to the current week (Monday–Friday) if no dates are provided. "
                "Use this for team availability overviews and weekly absence reports."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "start": {"type": "string", "description": "Start date YYYY-MM-DD (default: this Monday)"},
                    "end": {"type": "string", "description": "End date YYYY-MM-DD (default: this Friday)"},
                    "include_holidays": {"type": "boolean", "description": "Include public holidays (default: false)"},
                },
            },
        ),

        # ── CONTACTS ────────────────────────────────────────────────
        types.Tool(
            name="troi_list_contacts",
            description="List or search contacts in Troi.",
            inputSchema={
                "type": "object",
                "properties": {
                    "search": {"type": "string", "description": "Search contacts by name"},
                    "contact_type": {"type": "string", "description": "Filter by contact type"},
                },
            },
        ),

        types.Tool(
            name="troi_get_contact",
            description="Get details of a single contact by ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "contact_id": {"type": "integer", "description": "The contact ID"},
                },
                "required": ["contact_id"],
            },
        ),

        types.Tool(
            name="troi_create_contact",
            description=(
                "Create a contact in Troi (company, department, or person). "
                "Companies are top-level contacts. Departments are companies with a parent_contact_id "
                "pointing to the parent company. Persons have a parent company or department.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "name": {"type": "string", "description": "Company/department name, or last name for persons"},
                    "type": {"type": "string", "description": "Contact type: 'company' or 'person'. Departments use 'company' with parent_contact_id.", "enum": ["company", "person"]},
                    "parent_contact_id": {"type": "integer", "description": "Parent contact ID. For departments: parent company ID. For persons: company or department ID."},
                    "first_name": {"type": "string", "description": "First name (persons only)"},
                    "salutation": {"type": "string", "description": "Salutation: 'Herr' or 'Frau' (persons only)"},
                    "title": {"type": "string", "description": "Academic title like 'Dr.' (persons only)"},
                    "position": {"type": "string", "description": "Job title (persons only)"},
                    "email": {"type": "string", "description": "Email address"},
                    "phone": {"type": "string", "description": "Phone number"},
                    "mobile": {"type": "string", "description": "Mobile phone (persons only)"},
                    "website": {"type": "string", "description": "Company website (companies only)"},
                    "street": {"type": "string", "description": "Street address"},
                    "zip_code": {"type": "string", "description": "ZIP/postal code"},
                    "city": {"type": "string", "description": "City"},
                    "country": {"type": "string", "description": "Country name (default: Deutschland)"},
                    "country_iso": {"type": "string", "description": "ISO country code (default: DE)"},
                    "remark": {"type": "string", "description": "Free-text remark"},
                },
                "required": ["name", "type"],
            },
        ),

        types.Tool(
            name="troi_update_contact",
            description=(
                "Update an existing contact in Troi. Only specified fields are changed.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "contact_id": {"type": "integer", "description": "The contact ID to update"},
                    "data": {
                        "type": "object",
                        "description": "Fields to update. Supported: name, first_name, email, phone, mobile, website, street, zip_code, city, country, country_iso, salutation, title, position, remark, is_inactive.",
                    },
                },
                "required": ["contact_id", "data"],
            },
        ),

        # ── CALENDAR EVENTS ─────────────────────────────────────────
        types.Tool(
            name="troi_list_calendar_events",
            description="List calendar events from Troi. Can filter by date range and employee.",
            inputSchema={
                "type": "object",
                "properties": {
                    "start": {"type": "string", "description": "Start date YYYY-MM-DD"},
                    "end": {"type": "string", "description": "End date YYYY-MM-DD"},
                    "employee_id": {"type": "integer", "description": "Filter by employee ID"},
                },
            },
        ),

        # ── DOCUMENTS ───────────────────────────────────────────────
        types.Tool(
            name="troi_list_documents",
            description=(
                "List documents (offers, invoices) for a project. "
                "Returns document IDs and status. The document ID is needed "
                "for downloading PDFs (troi_download_offer_pdf), "
                "generating offer numbers (troi_generate_offer_number), "
                "and the approval workflow (troi_start_approval)."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "Filter by project ID"},
                },
            },
        ),

        types.Tool(
            name="troi_get_document",
            description="Get details of a single document by ID.",
            inputSchema={
                "type": "object",
                "properties": {
                    "document_id": {"type": "integer", "description": "The document ID"},
                },
                "required": ["document_id"],
            },
        ),

        # ── OFFER SUMMARY ──────────────────────────────────────────
        types.Tool(
            name="troi_get_offer_summary",
            description=(
                "Get a complete offer/quote summary for a project. "
                "Returns the project name, customer, offer number (Angebotsnummer), "
                "all positions grouped by phase (subproject) with line totals, "
                "and the grand total (Gesamtsumme netto). "
                "Use this to review an offer before approval or to report totals."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "project_id": {"type": "integer", "description": "The project ID to summarize"},
                },
                "required": ["project_id"],
            },
        ),

        # ── COMPOUND OFFER CREATION ───────────────────────────────
        types.Tool(
            name="troi_create_offer",
            description=(
                "Create a complete offer (project + phases + positions) in a single call. "
                "This replaces the need to call troi_create_project, troi_create_subproject, "
                "and troi_create_position_from_pricelist separately.\n\n"
                "Requires:\n"
                "- Customer hash ID (from troi_list_customers)\n"
                "- Price list item UUIDs (from troi_list_pricelist_items)\n\n"
                "Returns project_id, all subproject/position IDs, the offer total, "
                "and the PDF file path (auto-downloaded by default).\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "name": {"type": "string", "description": "Project/offer name (required)"},
                    "customer": {"type": "string", "description": "Customer hash ID (from troi_list_customers)"},
                    "project_type": {"type": "integer", "description": "Project type ID (default: 28 = Custom Projekt)", "default": 28},
                    "status_id": {"type": "integer", "description": "Project status ID (default: 55 = angeboten)", "default": 55},
                    "project_leader": {"type": "integer", "description": "Employee ID of project leader (required). Appears as 'AirLST:' on the offer PDF."},
                    "contact_id": {"type": "integer", "description": "Contact person ID (from troi_list_contacts). Shown as 'Ansprechpartner' on the PDF. Also set as Gesprächspartner on the project and default invoice recipient."},
                    "team_id": {"type": "integer", "description": "Team/project unit ID (default: 7 = AirLST All). Set on the project level."},
                    "invoice_recipient_id": {"type": "integer", "description": "Invoice recipient contact ID. Defaults to contact_id if not provided."},
                    "internal_description": {"type": "string", "description": "Internal project notes"},
                    "external_description": {"type": "string", "description": "Client-facing description (Kurzbeschreibung). Shown on the offer PDF and set on both project and document."},
                    "reporting_date": {"type": "string", "description": "Reporting/invoice date YYYY-MM-DD"},
                    "start_date": {"type": "string", "description": "Project start date YYYY-MM-DD"},
                    "end_date": {"type": "string", "description": "Project end date YYYY-MM-DD"},
                    "phases": {
                        "type": "array",
                        "description": "List of phases (subprojects), each containing positions",
                        "items": {
                            "type": "object",
                            "properties": {
                                "name": {"type": "string", "description": "Phase name (e.g. 'Lizenzen', 'Programmierung')"},
                                "positions": {
                                    "type": "array",
                                    "description": "Positions in this phase",
                                    "items": {
                                        "type": "object",
                                        "properties": {
                                            "pricelist_item_uuid": {"type": "string", "description": "Price list item UUID"},
                                            "quantity": {"type": "number", "description": "Quantity (default 1)", "default": 1},
                                            "sale_price_override": {"type": "number", "description": "Override sale price per unit (optional)"},
                                        },
                                        "required": ["pricelist_item_uuid"],
                                    },
                                },
                            },
                            "required": ["name", "positions"],
                        },
                    },
                    "auto_download_pdf": {
                        "type": "boolean",
                        "description": "Automatically download the offer PDF after creation (default: true). Set to false to skip.",
                        "default": True,
                    },
                },
                "required": ["name", "customer", "phases", "project_leader"],
            },
        ),

        # ── PDF DOWNLOAD ──────────────────────────────────────────
        types.Tool(
            name="troi_download_offer_pdf",
            description=(
                "Download an offer/quote as PDF from Troi. Uses Troi's built-in "
                "print workflow to generate and download the PDF file. "
                "Returns the local file path where the PDF was saved.\n\n"
                "Works for BOTH draft (unapproved) and approved documents. "
                "Draft offers will show 'Entwurf' watermark on the PDF, which is expected. "
                "No approval is required to download — use this to preview offers before approval.\n\n"
                "Requires the document_id (from troi_list_documents, not the project_id)."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "document_id": {"type": "integer", "description": "The document ID (from troi_list_documents)"},
                    "blank_copies": {"type": "integer", "description": "Copies on blank paper (default 1)", "default": 1},
                    "stationery_copies": {"type": "integer", "description": "Copies on letterhead (default 0)", "default": 0},
                    "form_id": {"type": "integer", "description": "Form template ID (45=Angebot, default 45)", "default": 45},
                    "output_path": {"type": "string", "description": "Custom output file path (optional, defaults to /tmp/)"},
                },
                "required": ["document_id"],
            },
        ),

        types.Tool(
            name="troi_generate_offer_number",
            description=(
                "Assign an offer number (Belegnummer/Angebotsnummer) to a document and "
                "download the final PDF. This is the 'Nummer vergeben und drucken' workflow.\n\n"
                "IMPORTANT: This is IRREVERSIBLE — once a number is assigned, it cannot be undone. "
                "The document MUST be approved (Status 'A') before a number can be generated.\n\n"
                "Workflow: 1) troi_list_documents to find the document, "
                "2) verify it is approved (Status 'A') and has no number yet, "
                "3) this tool to assign the number and download the PDF.\n\n"
                "Returns the assigned Angebotsnummer, PDF file path, and file size.\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute. "
                "WARNING: This action is IRREVERSIBLE."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "document_id": {"type": "integer", "description": "The document ID (from troi_list_documents). Must be approved (Status 'A')."},
                    "blank_copies": {"type": "integer", "description": "Copies on Briefpapier/letterhead (default 1)", "default": 1},
                    "stationery_copies": {"type": "integer", "description": "Copies on Blankopapier/blank paper (default 0)", "default": 0},
                    "form_id": {"type": "integer", "description": "Form template ID (45=Angebot, default 45)", "default": 45},
                    "output_path": {"type": "string", "description": "Custom output file path (optional, defaults to ~/Downloads/)"},
                },
                "required": ["document_id"],
            },
        ),

        # ── APPROVAL WORKFLOW ──────────────────────────────────────
        types.Tool(
            name="troi_start_approval",
            description=(
                "Submit a document (offer/quote) for approval. Sends the offer to the "
                "specified approver employee. The approval appears in their Troi dashboard.\n\n"
                "Workflow: 1) troi_list_documents to find the document ID, "
                "2) optionally troi_get_approval_info to see available approvers, "
                "3) this tool to start the approval.\n\n"
                "IMPORTANT — After successful approval, you MUST notify the approver via Slack:\n"
                "1. Use the returned 'approver_name' to search for the person in Slack (slack_search_users).\n"
                "2. Send them a DM (slack_send_message) with:\n"
                "   - Project name and offer number (Angebotsnummer)\n"
                "   - Offer total amount\n"
                "   - Approval deadline\n"
                "   - Direct link to the Troi project (troi_project_url from the response)\n\n"
                "Server-enforced: a preview is returned first. Re-call with confirmed=true to execute. "
                "WARNING: This action is IRREVERSIBLE."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "document_id": {"type": "integer", "description": "The document ID to submit for approval (from troi_list_documents)"},
                    "approver_employee_id": {"type": "integer", "description": "Employee ID of the approver"},
                    "deadline": {"type": "string", "description": "Approval deadline in DD.MM.YYYY format (optional)"},
                },
                "required": ["document_id", "approver_employee_id"],
            },
        ),

        types.Tool(
            name="troi_get_approval_info",
            description=(
                "Get approval information for a document: who can approve it, "
                "what type of approval process it uses. Use this before troi_start_approval "
                "to show the user available approvers."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "document_id": {"type": "integer", "description": "The document ID to check"},
                },
                "required": ["document_id"],
            },
        ),

        # ── BOOKING YEARS ───────────────────────────────────────────
        types.Tool(
            name="troi_list_booking_years",
            description="List all booking years configured in Troi.",
            inputSchema={"type": "object", "properties": {}},
        ),
    ]

    # --- Auto-inject 'confirmed' parameter into all write tools ---
    for tool in tools:
        if tool.name in WRITE_TOOLS:
            tier = WRITE_TOOLS[tool.name]
            desc = (
                "Set to true to execute this operation. "
                "Without this flag, the server returns a preview instead of executing."
            )
            if tier == "dangerous":
                desc += " WARNING: This action is IRREVERSIBLE."
            tool.inputSchema["properties"]["confirmed"] = {
                "type": "boolean",
                "description": desc,
            }

    return tools


# ---------------------------------------------------------------------------
# Tool execution
# ---------------------------------------------------------------------------

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    try:
        # --- Permission gate: require confirmed=true for write operations ---
        if name in WRITE_TOOLS and not arguments.get("confirmed"):
            return [types.TextContent(type="text", text=_build_preview(name, arguments))]

        c = _init_client()

        # ── Projects
        if name == "troi_list_projects":
            return _ok(c.list_projects(
                search=arguments.get("search"),
                customer_id=arguments.get("customer_id"),
                in_process=arguments.get("in_process"),
                project_status_id=arguments.get("project_status_id"),
                project_leader_id=arguments.get("project_leader_id"),
                size=arguments.get("size", 200),
            ))
        elif name == "troi_get_project":
            return _ok(c.get_project(arguments["project_id"]))
        elif name == "troi_create_project":
            data: dict[str, Any] = {"name": arguments["name"], "customer": arguments["customer"]}
            if arguments.get("project_leader"):
                data["projectLeader"] = arguments["project_leader"]
            if arguments.get("project_type"):
                data["projectType"] = arguments["project_type"]
            if arguments.get("reporting_date"):
                data["reportingDate"] = arguments["reporting_date"]
            if arguments.get("start_date"):
                data["projectStartDate"] = arguments["start_date"]
            if arguments.get("end_date"):
                data["projectEndDate"] = arguments["end_date"]
            result = c.create_project(data)
            # Set status + descriptions via REST PUT (not supported by component create)
            proj_id = result.get("id")
            if proj_id:
                post_update: dict[str, Any] = {}
                status_id = arguments.get("status_id", 55)
                post_update["Status"] = {"Path": f"/misc/projectStatuses/{status_id}"}
                if arguments.get("external_description"):
                    post_update["ExternalDescription"] = arguments["external_description"]
                if arguments.get("internal_description"):
                    post_update["InternalDescription"] = arguments["internal_description"]
                c.update_project(proj_id, post_update)
                result = c.get_project(proj_id)
            return _ok(result)
        elif name == "troi_update_project":
            return _ok(c.update_project(arguments["project_id"], arguments["data"]))
        elif name == "troi_delete_project":
            return _ok(c.delete_project(arguments["project_id"]))

        # ── Subprojects
        elif name == "troi_list_subprojects":
            return _ok(c.list_subprojects(project_id=arguments.get("project_id")))
        elif name == "troi_get_subproject":
            return _ok(c.get_subproject(arguments["subproject_id"]))
        elif name == "troi_create_subproject":
            return _ok(c.create_subproject(
                project_id=arguments["project_id"],
                name=arguments["name"],
                project_type_id=arguments.get("project_type_id", 28),
            ))
        elif name == "troi_update_subproject":
            return _ok(c.update_subproject(arguments["subproject_id"], arguments["data"]))
        elif name == "troi_delete_subproject":
            return _ok(c.delete_subproject(
                arguments["subproject_id"],
                project_id=arguments.get("project_id"),
            ))

        # ── Positions
        elif name == "troi_list_positions":
            return _ok(c.list_calculation_positions(
                project_id=arguments.get("project_id"),
                search=arguments.get("search"),
                favorites_only=arguments.get("favorites_only", False),
            ))
        elif name == "troi_get_position":
            return _ok(c.get_calculation_position(arguments["position_id"]))
        elif name == "troi_create_position":
            return _ok(c.create_calculation_position(arguments["data"]))
        elif name == "troi_update_position":
            return _ok(c.update_calculation_position(arguments["position_id"], arguments["data"]))
        elif name == "troi_delete_position":
            return _ok(c.delete_calculation_position(arguments["position_id"]))

        # ── Price List
        elif name == "troi_list_pricelist_groups":
            return _ok(c.list_pricelist_groups())
        elif name == "troi_list_pricelist_items":
            return _ok(c.list_pricelist_items(group_id=arguments.get("group_id")))
        elif name == "troi_create_position_from_pricelist":
            return _ok(c.create_calculation_position_from_pricelist(
                project_id=arguments["project_id"],
                subproject_id=arguments["subproject_id"],
                item_uuid=arguments["item_uuid"],
                group_id=arguments.get("group_id"),
                quantity=arguments.get("quantity", 1),
                sale_price_override=arguments.get("sale_price_override"),
            ))

        # ── Units & Reference Data
        elif name == "troi_list_units":
            return _ok(c.list_units())
        elif name == "troi_list_project_types":
            return _ok(c.list_project_types())
        elif name == "troi_list_project_statuses":
            return _ok(c.list_project_statuses())

        # ── Time entries
        elif name == "troi_list_time_entries":
            return _ok(c.list_time_entries(
                project_id=arguments.get("project_id"),
                calculation_position_id=arguments.get("calculation_position_id"),
                start_date=arguments.get("start_date"),
                end_date=arguments.get("end_date"),
            ))
        elif name == "troi_get_time_entry":
            return _ok(c.get_time_entry(arguments["entry_id"]))
        elif name == "troi_create_time_entry":
            return _ok(c.create_time_entry(arguments["data"]))
        elif name == "troi_update_time_entry":
            return _ok(c.update_time_entry(arguments["entry_id"], arguments["data"]))
        elif name == "troi_delete_time_entry":
            return _ok(c.delete_time_entry(arguments["entry_id"]))

        # ── Employees
        elif name == "troi_list_employees":
            return _ok(c.list_employees(show_inactive=arguments.get("show_inactive", False)))
        elif name == "troi_get_employee":
            return _ok(c.get_employee(arguments["employee_id"]))

        # ── Customers
        elif name == "troi_list_customers":
            return _ok(c.list_customers(is_active=arguments.get("is_active")))
        elif name == "troi_get_customer":
            return _ok(c.get_customer(arguments["customer_id"]))
        elif name == "troi_create_customer":
            return _ok(c.create_customer(
                name=arguments["name"],
                contact_id=arguments["contact_id"],
                shortcut=arguments["shortcut"],
                is_active=arguments.get("is_active", True),
                payment_term_id=arguments.get("payment_term_id"),
                vat_number=arguments.get("vat_number"),
                tax_number=arguments.get("tax_number"),
            ))
        elif name == "troi_update_customer":
            return _ok(c.update_customer(
                customer_id=arguments["customer_id"],
                data=arguments["data"],
            ))
        elif name == "troi_create_customer_with_contact":
            return _ok(c.create_customer_with_contact(arguments))

        # ── Clients
        elif name == "troi_list_clients":
            return _ok(c.list_clients())
        elif name == "troi_get_client":
            return _ok(c.get_client(arguments["client_id"]))

        # ── Absences
        elif name == "troi_list_absences":
            return _ok(c.list_absences(
                start=arguments.get("start"),
                end=arguments.get("end"),
                employee_id=arguments.get("employee_id"),
            ))

        elif name == "troi_get_team_absences":
            from datetime import date, timedelta
            today = date.today()
            # Default to current week Mon-Fri
            monday = today - timedelta(days=today.weekday())
            friday = monday + timedelta(days=4)
            start = arguments.get("start") or monday.isoformat()
            end = arguments.get("end") or friday.isoformat()
            return _ok(c.get_team_absences(
                start=start,
                end=end,
                include_holidays=arguments.get("include_holidays", False),
            ))

        # ── Contacts
        elif name == "troi_list_contacts":
            return _ok(c.list_contacts(
                search=arguments.get("search"),
                contact_type=arguments.get("contact_type"),
            ))
        elif name == "troi_get_contact":
            return _ok(c.get_contact(arguments["contact_id"]))
        elif name == "troi_create_contact":
            return _ok(c.create_contact(
                name=arguments["name"],
                contact_type=arguments["type"],
                parent_contact_id=arguments.get("parent_contact_id"),
                first_name=arguments.get("first_name"),
                salutation=arguments.get("salutation"),
                title=arguments.get("title"),
                position=arguments.get("position"),
                email=arguments.get("email"),
                phone=arguments.get("phone"),
                mobile=arguments.get("mobile"),
                website=arguments.get("website"),
                street=arguments.get("street"),
                zip_code=arguments.get("zip_code"),
                city=arguments.get("city"),
                country=arguments.get("country"),
                country_iso=arguments.get("country_iso"),
                remark=arguments.get("remark"),
            ))
        elif name == "troi_update_contact":
            return _ok(c.update_contact(
                contact_id=arguments["contact_id"],
                data=arguments["data"],
            ))

        # ── Calendar
        elif name == "troi_list_calendar_events":
            return _ok(c.list_calendar_events(
                start=arguments.get("start"),
                end=arguments.get("end"),
                employee_id=arguments.get("employee_id"),
            ))

        # ── Documents
        elif name == "troi_list_documents":
            return _ok(c.list_documents(project_id=arguments.get("project_id")))
        elif name == "troi_get_document":
            return _ok(c.get_document(arguments["document_id"]))

        # ── Offer summary
        elif name == "troi_get_offer_summary":
            return _ok(c.get_offer_summary(arguments["project_id"]))

        # ── Compound offer creation
        elif name == "troi_create_offer":
            return _ok(c.create_offer(arguments))

        # ── PDF download
        elif name == "troi_download_offer_pdf":
            result = c.download_offer_pdf(
                document_id=arguments["document_id"],
                blank_copies=arguments.get("blank_copies", 1),
                stationery_copies=arguments.get("stationery_copies", 0),
                form_id=arguments.get("form_id", 45),
                output_path=arguments.get("output_path"),
            )
            return _ok(result)

        # ── Generate offer number
        elif name == "troi_generate_offer_number":
            result = c.generate_offer_number(
                document_id=arguments["document_id"],
                blank_copies=arguments.get("blank_copies", 1),
                stationery_copies=arguments.get("stationery_copies", 0),
                form_id=arguments.get("form_id", 45),
                output_path=arguments.get("output_path"),
            )
            return _ok(result)

        # ── Approval workflow
        elif name == "troi_start_approval":
            return _ok(c.start_approval(
                document_id=arguments["document_id"],
                approver_employee_id=arguments["approver_employee_id"],
                deadline=arguments.get("deadline"),
            ))
        elif name == "troi_get_approval_info":
            return _ok(c.get_approval_form(arguments["document_id"]))

        # ── Booking years
        elif name == "troi_list_booking_years":
            return _ok(c.list_booking_years())

        else:
            return _err(ValueError(f"Unknown tool: {name}"))

    except TroiError as e:
        return _err(e)
    except Exception as e:
        return _err(e)


# ---------------------------------------------------------------------------
# Main entry point
# ---------------------------------------------------------------------------

def main():
    asyncio.run(_run())


async def _run():
    async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
        await server.run(read_stream, write_stream,
                         server.create_initialization_options())


if __name__ == "__main__":
    main()
