{
  "format": "galaxyunit.capabilities/1",
  "product": "Galaxy Unit",
  "site": "https://galaxyunit.app",
  "status": "rest-v1 + mcp + oauth: REST /api/v1 and MCP https://app.galaxyunit.app/api/mcp are live with API keys or OAuth 2.1 — see https://galaxyunit.app/for-agents",
  "mcp": {
    "url": "https://app.galaxyunit.app/api/mcp",
    "transport": "streamable-http",
    "auth": "oauth2.1 (PKCE, CIMD) or bearer api key",
    "protected_resource_metadata": "https://app.galaxyunit.app/.well-known/oauth-protected-resource/api/mcp"
  },
  "roles": [
    "owner",
    "admin",
    "helper",
    "accountant"
  ],
  "capabilities": [
    {
      "name": "find_contact",
      "kind": "read",
      "permission": "contacts.read.billing",
      "description": "Look up a client by ANY handle the owner remembers — person name, company, boat/object name or location (\"клиент с лодкой Милос\", \"the guy at Port Credit\") — to get their phone, email, location, object and size. Use whenever the user asks about a specific client. A miss returns similar existing clients to offer.",
      "parameters": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "The name fragment the user gave — person, company, boat/object or location"
          }
        },
        "required": [
          "query"
        ]
      }
    },
    {
      "name": "get_schedule",
      "kind": "read",
      "permission": "jobs.read",
      "description": "Get jobs for a date or date range. Use for \"what's on today/tomorrow/this week\" and for a NAMED day (\"what do I have on Thursday\", \"на пятницу\") — for a named day pass `date`, resolved against TODAY in the context, not `range`.",
      "parameters": {
        "type": "object",
        "properties": {
          "range": {
            "type": "string",
            "enum": [
              "today",
              "tomorrow",
              "week",
              "next2weeks",
              "any"
            ],
            "description": "default week. \"any\" drops the date window entirely — use it when the question is about state, not timing (\"how many active jobs do I have\")"
          },
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD — a single specific day. Wins over range. Work the date out from TODAY in the context (e.g. \"Thursday\" = the next Thursday on or after today)"
          },
          "to_date": {
            "type": "string",
            "description": "YYYY-MM-DD — with `date`, an explicit range end (\"from Monday to Wednesday\")"
          },
          "stage": {
            "type": "array",
            "description": "Keep only these stages. Use for questions about WORK STATE rather than a date — \"how many active jobs\" is [\"scheduled\",\"in_progress\"], \"what did I lose\" is [\"lost\"]. Pair it with range:\"any\" when the question has no date in it at all.",
            "items": {
              "type": "string",
              "enum": [
                "lead",
                "quoted",
                "scheduled",
                "in_progress",
                "completed",
                "cancelled",
                "weather_hold",
                "lost"
              ]
            }
          }
        }
      }
    },
    {
      "name": "get_unpaid",
      "kind": "read",
      "permission": "invoices.read",
      "description": "List invoices that still owe money (sent/overdue/partially paid) with balances. Use for \"who hasn't paid\", AR questions.",
      "parameters": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "get_spend",
      "kind": "read",
      "permission": "expenses.read",
      "description": "Total expenses for a period, broken down by category — the OUTGOING side and nothing else. Use for \"how much did I spend\", \"what went on fuel this month\". NEVER for profit, margin or \"what did I earn\": profit needs the incoming side too and belongs to get_revenue, which already includes expenses.",
      "parameters": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "enum": [
              "month",
              "quarter",
              "year"
            ],
            "description": "default month"
          }
        }
      }
    },
    {
      "name": "get_price_list",
      "kind": "read",
      "permission": "settings.services.read",
      "description": "The business price list: unit RATES per service. Use only to SHOW the menu (\"what services do we have\", \"what is our rate for X\"). To price an actual job for a client — any question with a size, a quantity or a total in it — call `estimate` instead: it returns the same rates already multiplied and taxed on the server.",
      "parameters": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "get_job_details",
      "kind": "read",
      "permission": "jobs.read",
      "description": "Full details of specific job(s) by client name or job title: price, deposit, services, workers with hours, payroll, expenses and profit. Use for \"how did the X job do\", \"who worked on…\", job profit questions.",
      "parameters": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "Client name or part of the job title"
          }
        },
        "required": [
          "query"
        ]
      }
    },
    {
      "name": "get_client_history",
      "kind": "read",
      "permission": "invoices.read",
      "description": "Everything about one client: all their jobs, invoices, how much they were billed and paid lifetime. Use for \"history with X\", \"how much has X spent with us\".",
      "parameters": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "Part of the client name"
          }
        },
        "required": [
          "query"
        ]
      }
    },
    {
      "name": "get_revenue",
      "kind": "read",
      "permission": "analytics.read",
      "description": "The WHOLE money picture for a period: payments received, amount invoiced, expenses, and profit — both sides at once. Use for \"how much did I make\", \"какой профит\", revenue, profit and margin questions. Every PROFIT question comes here and never to get_spend: this already carries the expense figure, so there is nothing to fetch separately.",
      "parameters": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "enum": [
              "month",
              "quarter",
              "year"
            ],
            "description": "default month"
          }
        }
      }
    },
    {
      "name": "get_invoice",
      "kind": "read",
      "permission": "invoices.read",
      "description": "One invoice in full by its number: line items, payments, balance. Use for \"what's on invoice X\", \"is INV-… paid\".",
      "parameters": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Invoice number like INV-2026-0007 (partial ok)"
          }
        },
        "required": [
          "number"
        ]
      }
    },
    {
      "name": "estimate",
      "kind": "read",
      "permission": "settings.services.read",
      "description": "Price a set of services into a quote-ready estimate using the business price list (per-unit, hourly and flat pricing, tax included in the total). Use ONLY when the owner is ASKING what something costs: \"how much for X and Y\", \"quote a hull polish for a 42-footer\", \"what would that cost\". If they are TELLING you to book/create/schedule a job — even one with prices in the sentence — call create_job instead; this tool books nothing. The server does ALL the math from the real price list — never multiply, add or invent a price yourself, not even for a single line.",
      "parameters": {
        "type": "object",
        "properties": {
          "services": {
            "type": "array",
            "description": "Services to price",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "EXACT service name as stored in the price list (matching is literal — the list is often in English while the user speaks another language, so pass the stored name, not a translation). Unsure of the exact wording? Call get_price_list first."
                },
                "qty": {
                  "type": "number",
                  "description": "How many (default 1). For size-priced services leave empty — the size drives it"
                },
                "hours": {
                  "type": "number",
                  "description": "Hours, for hourly-priced services"
                }
              },
              "required": [
                "name"
              ]
            }
          },
          "size": {
            "type": "number",
            "description": "Object size in the business unit (boat length ft, area sqft…) — drives size-based pricing; omit to use the client's stored size"
          },
          "client": {
            "type": "string",
            "description": "Client name, to pull their stored size and address them"
          }
        },
        "required": [
          "services"
        ]
      }
    },
    {
      "name": "get_workers",
      "kind": "read",
      "permission": "expenses.read",
      "description": "Workers/crew: rates and this period's labor pay, split into paid vs unpaid. Use for \"who do I owe\", payroll questions.",
      "parameters": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "enum": [
              "month",
              "quarter",
              "year"
            ],
            "description": "default month"
          }
        }
      }
    },
    {
      "name": "get_tasks",
      "kind": "read",
      "permission": "jobs.read",
      "description": "The to-do list: what is due today, what is overdue, what is coming. Use for \"what do I need to do\", \"any tasks today\", \"what did I forget\".",
      "parameters": {
        "type": "object",
        "properties": {
          "scope": {
            "type": "string",
            "enum": [
              "today",
              "overdue",
              "week",
              "all"
            ],
            "description": "default today"
          }
        }
      }
    },
    {
      "name": "open_page",
      "kind": "read",
      "permission": "copilot.use",
      "description": "Navigate the user to a Galaxy page: a client's CRM profile, a job, an invoice, or a main section. Use ONLY when the user explicitly asks to OPEN / GO TO / SHOW the page (\"открой клиента X\", \"open the Nuno job\", \"покажи счёт INV-…\", \"открой календарь\"). Do NOT use it to answer data questions (\"who owes me\", \"what's this week\") or \"where do I change X\" questions — answer those with words instead.",
      "parameters": {
        "type": "object",
        "properties": {
          "target": {
            "type": "string",
            "enum": [
              "client",
              "job",
              "invoice",
              "section"
            ],
            "description": "What to open"
          },
          "query": {
            "type": "string",
            "description": "Client name / job title or client / invoice number; for section — one of: dashboard, crm, calendar, tasks, jobs, quotes, invoices, expenses, workers, materials, analytics, bookkeeper, settings"
          }
        },
        "required": [
          "target",
          "query"
        ]
      }
    },
    {
      "name": "create_expense",
      "kind": "write",
      "permission": "expenses.write",
      "description": "Record a business expense. Use for \"log/record an expense\", \"I spent X on Y\".",
      "parameters": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "description": "Total amount in CAD — must be greater than 0"
          },
          "category": {
            "type": "string",
            "description": "Category KEY copied EXACTLY from the \"Expense category keys\" list in the INDUSTRY line of the system prompt — never invent a key; unknown keys fall back to other"
          },
          "description": {
            "type": "string"
          },
          "vendor": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD; default today"
          }
        },
        "required": [
          "amount"
        ]
      }
    },
    {
      "name": "create_contact",
      "kind": "write",
      "permission": "contacts.write",
      "description": "Add a client/contact. Use for \"add a client\", \"new customer\".",
      "parameters": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "company_name": {
            "type": "string"
          },
          "marina": {
            "type": "string",
            "description": "Location (marina / site / address — whatever this industry uses)"
          },
          "boat_name": {
            "type": "string",
            "description": "Object reference (boat / vehicle / property name)"
          },
          "boat_length_ft": {
            "type": "number",
            "description": "Object size (boat length ft, area sqft, …) — drives size-based pricing. Must be > 0 and <= 9999; omit it rather than guessing"
          },
          "notes": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ]
      }
    },
    {
      "name": "create_task",
      "kind": "write",
      "permission": "jobs.write",
      "description": "Add a to-do/task for a date, optionally at a time. Use for \"remind me to…\", \"add a task\", \"task tomorrow at 9\".",
      "parameters": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD; default today"
          },
          "start_time": {
            "type": "string",
            "description": "HH:MM 24h — only when the user names a time"
          },
          "end_time": {
            "type": "string",
            "description": "HH:MM 24h — only when the user gives an end as well; needs start_time"
          },
          "job_title": {
            "type": "string",
            "description": "Attach the task to an existing job — client name or job title"
          }
        },
        "required": [
          "title"
        ]
      }
    },
    {
      "name": "create_job",
      "kind": "write",
      "permission": "jobs.write",
      "description": "Create a job/booking for a client. Can set price, deposit, services and assigned workers with hours. Use for \"book a job\", \"schedule…\", \"create a job for X at $Y with worker Z for N hours\". BOOK FIRST: a job needs only a client, a title and a date — do NOT ask for size, price or deposit before creating it, and do not withhold the booking because pricing is unknown. Always pass the requested service as a services line; give a price ONLY when the owner said one — otherwise OMIT price and the server takes it from the price list (per-foot services are multiplied by the client's boat length on the server). Never invent a price.",
      "parameters": {
        "type": "object",
        "properties": {
          "client_name": {
            "type": "string",
            "description": "Existing client name (match from data)"
          },
          "title": {
            "type": "string"
          },
          "start_date": {
            "type": "string",
            "description": "YYYY-MM-DD"
          },
          "end_date": {
            "type": "string",
            "description": "YYYY-MM-DD; default same as start"
          },
          "marina": {
            "type": "string",
            "description": "Location (marina / site / address)"
          },
          "description": {
            "type": "string"
          },
          "job_type": {
            "type": "string"
          },
          "total_price": {
            "type": "number",
            "description": "Total job price in CAD (if not itemized by services)"
          },
          "deposit_amount": {
            "type": "number",
            "description": "Deposit in CAD"
          },
          "services": {
            "type": "array",
            "description": "Line-item services; price optional (price list fills it)",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "price": {
                  "type": "number",
                  "description": "Unit price CAD — only if the owner said it; omit to take the price-list price"
                },
                "qty": {
                  "type": "number",
                  "description": "default 1"
                }
              },
              "required": [
                "name"
              ]
            }
          },
          "workers": {
            "type": "array",
            "description": "Assigned workers with hours and hourly rate",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "hours": {
                  "type": "number"
                },
                "rate": {
                  "type": "number",
                  "description": "Hourly rate CAD"
                }
              },
              "required": [
                "name",
                "hours"
              ]
            }
          }
        },
        "required": [
          "client_name",
          "title",
          "start_date"
        ]
      }
    },
    {
      "name": "add_payment",
      "kind": "write",
      "permission": "payments.write",
      "description": "Record a payment against an invoice. Use for \"mark invoice X paid\", \"got paid\".",
      "parameters": {
        "type": "object",
        "properties": {
          "invoice_number": {
            "type": "string",
            "description": "Invoice number like INV-2026-0007"
          },
          "amount": {
            "type": "number",
            "description": "CAD; default = the invoice balance"
          },
          "method": {
            "type": "string",
            "enum": [
              "etransfer",
              "cash",
              "cheque",
              "credit_card",
              "bank_transfer",
              "other"
            ]
          },
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD; default today"
          }
        },
        "required": [
          "invoice_number"
        ]
      }
    },
    {
      "name": "remember_note",
      "kind": "write",
      "permission": "copilot.use",
      "description": "Save a lasting preference/fact about this business for future chats.",
      "parameters": {
        "type": "object",
        "properties": {
          "note": {
            "type": "string"
          }
        },
        "required": [
          "note"
        ]
      }
    },
    {
      "name": "complete_task",
      "kind": "write",
      "permission": "jobs.write",
      "description": "Mark a to-do / checklist item as DONE (or undone). Use whenever the user reports something is finished or bought: \"I bought the compound\", \"mark X done\", \"отметь что сделано\", \"я купил краску — отметь\", \"uncheck X\". This is the tool for \"done\" — not log_job_hours (worker hours) and not create_task (a new to-do).",
      "parameters": {
        "type": "object",
        "properties": {
          "task_query": {
            "type": "string",
            "description": "Words from the task title — matched against open tasks"
          },
          "done": {
            "type": "boolean",
            "description": "true = done (default), false = reopen it"
          }
        },
        "required": [
          "task_query"
        ]
      }
    },
    {
      "name": "reschedule_task",
      "kind": "write",
      "permission": "jobs.write",
      "description": "Move a to-do to another day, or set/clear its time. Use for \"push X to tomorrow\", \"move X to Friday at 9\".",
      "parameters": {
        "type": "object",
        "properties": {
          "task_query": {
            "type": "string",
            "description": "Words from the task title — matched against open tasks"
          },
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD — the new day"
          },
          "start_time": {
            "type": "string",
            "description": "HH:MM 24h; omit to leave the time alone, pass \"\" to clear it"
          },
          "end_time": {
            "type": "string",
            "description": "HH:MM 24h"
          }
        },
        "required": [
          "task_query"
        ]
      }
    },
    {
      "name": "draft_message",
      "kind": "draft",
      "permission": "contacts.read.billing",
      "description": "Compose a ready-to-send message DRAFT to a client (payment reminder, arrival heads-up, quote follow-up, thank-you…). NOTHING is sent — the owner gets the text with Copy/Share buttons and sends it themselves. Write in the language the CLIENT is spoken to (clients here are usually English-speaking even when the owner talks to you in another language; follow any stored note about the client's language). CRITICAL: any amount, invoice number, date or balance in the draft must be copied EXACTLY from a read tool result in this conversation — call get_unpaid / get_invoice / get_client_history first if you don't have the figure; never compute or recall money from memory.",
      "parameters": {
        "type": "object",
        "properties": {
          "client": {
            "type": "string",
            "description": "Client name — used to attach their phone number for sharing"
          },
          "text": {
            "type": "string",
            "description": "The complete message text, ready to send, in the client's language. Sign off with the REAL business name from the context — never a placeholder like [Your Business Name]"
          },
          "subject": {
            "type": "string",
            "description": "Short label of what this is, in the owner's language (e.g. \"напоминание об оплате INV-2026-0007\")"
          }
        },
        "required": [
          "client",
          "text"
        ]
      }
    },
    {
      "name": "create_expense_category",
      "kind": "write",
      "permission": "expenses.write",
      "description": "Add a NEW expense category to this business. Use ONLY when the user EXPLICITLY asks to add/create a category (\"добавь категорию разрешения\", \"make a Permits category\"). Never call it while logging an expense — create_expense handles that on its own with the existing keys. If a same-meaning category already exists in any language, use the existing one; the server refuses lookalikes and returns the existing category instead.",
      "parameters": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Category name exactly as the user calls it (their language is fine)"
          }
        },
        "required": [
          "label"
        ]
      }
    },
    {
      "name": "update_job",
      "kind": "write",
      "permission": "jobs.write",
      "description": "Change an EXISTING job: its stage, dates, title, location, price, deposit or notes, or its service lines (add / reprice / remove a line; the total is recomputed on the server). Use for \"move X to scheduled\", \"перенеси на пятницу\", \"поменяй этап\", \"raise the price on Y\". Only pass the fields being changed — everything omitted is left exactly as it is. To create a NEW job use create_job; for worker hours use log_job_hours.",
      "parameters": {
        "type": "object",
        "properties": {
          "job": {
            "type": "string",
            "description": "Which job — the client name or the job title, however the user referred to it"
          },
          "stage": {
            "type": "string",
            "enum": [
              "lead",
              "quoted",
              "scheduled",
              "in_progress",
              "completed",
              "cancelled",
              "weather_hold",
              "lost"
            ],
            "description": "The new stage. These are the ONLY valid values — never invent one"
          },
          "title": {
            "type": "string"
          },
          "start_date": {
            "type": "string",
            "description": "YYYY-MM-DD"
          },
          "end_date": {
            "type": "string",
            "description": "YYYY-MM-DD"
          },
          "start_time": {
            "type": "string",
            "description": "HH:MM 24h"
          },
          "end_time": {
            "type": "string",
            "description": "HH:MM 24h"
          },
          "marina": {
            "type": "string",
            "description": "Location (marina / site / address)"
          },
          "deposit_amount": {
            "type": "number",
            "description": "Deposit in CAD"
          },
          "notes": {
            "type": "string"
          },
          "total_price": {
            "type": "number",
            "description": "Whole-job price in CAD — ONLY for a job that has no service lines (\"поставь цену 1200\"). If the job is itemized, change a line instead (set_service_price / add_services); the server refuses a bare total on an itemized job."
          },
          "add_services": {
            "type": "array",
            "description": "Lines to ADD to the job",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Service name as the user said it"
                },
                "unit_price": {
                  "type": "number",
                  "description": "Unit price in CAD — only if the user said a price; omit to take it from the price list"
                },
                "qty": {
                  "type": "number",
                  "description": "default 1"
                }
              },
              "required": [
                "name"
              ]
            }
          },
          "set_service_price": {
            "type": "array",
            "description": "Change the price of existing lines",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Existing line on the job"
                },
                "unit_price": {
                  "type": "number",
                  "description": "New unit price in CAD"
                }
              },
              "required": [
                "name",
                "unit_price"
              ]
            }
          },
          "remove_services": {
            "type": "array",
            "description": "Names of lines to REMOVE from the job",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "job"
        ]
      }
    },
    {
      "name": "log_job_hours",
      "kind": "write",
      "permission": "workers.log_hours",
      "description": "PAYROLL ONLY: record hours a WORKER spent on an existing job — \"добавь Диме 3 часа на джоб Нуно\", \"log 4 hours for Max on the Roscoe job\". Requires a worker NAME and a number of HOURS in the request. NOT for marking tasks or checklist items done (that is complete_task), not for creating jobs (create_job), not for prices or services (update_job). The pay rate comes from the worker card on the server — never pass a rate.",
      "parameters": {
        "type": "object",
        "properties": {
          "job": {
            "type": "string",
            "description": "Which job — the client name or the job title, however the user referred to it"
          },
          "workers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Worker name as the user said it"
                },
                "hours": {
                  "type": "number",
                  "description": "Hours to add"
                }
              },
              "required": [
                "name",
                "hours"
              ]
            }
          }
        },
        "required": [
          "job",
          "workers"
        ]
      }
    },
    {
      "name": "update_contact",
      "kind": "write",
      "permission": "contacts.write",
      "description": "Change an EXISTING client/contact: phone, email, location (marina), object (boat) name / size / model / year, slip, notes, billing address, company or name. Use for \"поменяй телефон Janet\", \"Sarah moved to Bluffers Park\", \"update Mike's boat length to 40\". Only pass the fields being changed — everything omitted is left exactly as it is. NEVER create a second contact to \"fix\" one; to add a NEW client use create_contact. For jobs use update_job.",
      "parameters": {
        "type": "object",
        "properties": {
          "contact": {
            "type": "string",
            "description": "Which client — the name (or boat / company) however the user referred to them"
          },
          "name": {
            "type": "string",
            "description": "New name — only when the user is renaming the client"
          },
          "phone": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "company_name": {
            "type": "string"
          },
          "marina": {
            "type": "string",
            "description": "Location (marina / site / address — whatever this industry uses)"
          },
          "boat_name": {
            "type": "string",
            "description": "Object reference (boat / vehicle / property name)"
          },
          "boat_length_ft": {
            "type": "number",
            "description": "Object size (boat length ft, area sqft, …) — must be > 0 and <= 9999"
          },
          "boat_model": {
            "type": "string"
          },
          "boat_year": {
            "type": "integer",
            "description": "4-digit year"
          },
          "dock_slip": {
            "type": "string",
            "description": "Slip / unit / bay number"
          },
          "notes": {
            "type": "string",
            "description": "REPLACES the notes field"
          },
          "billing_address": {
            "type": "string"
          },
          "billing_city": {
            "type": "string"
          },
          "billing_province": {
            "type": "string"
          },
          "billing_postal": {
            "type": "string"
          },
          "billing_email": {
            "type": "string"
          },
          "client_hst_number": {
            "type": "string",
            "description": "Client tax number as printed on their documents"
          }
        },
        "required": [
          "contact"
        ]
      }
    },
    {
      "name": "create_quote",
      "kind": "write",
      "permission": "quotes.write",
      "description": "Create a QUOTE (estimate) for a client as a draft document. Use for \"send a quote\", \"quote Kai for…\", \"сделай квоту / смету\". Either name the JOB to quote (its service lines are copied) or give the client and the line items with prices. Never invent a price: take it from what the user said or from get_price_list, otherwise ask. Tax comes from the business settings; the card shows lines and total before anything is created.",
      "parameters": {
        "type": "object",
        "properties": {
          "client_name": {
            "type": "string",
            "description": "Client name (match from data). Required unless job is given"
          },
          "job": {
            "type": "string",
            "description": "An existing job to quote — its title or its client name; the job's service lines are copied"
          },
          "items": {
            "type": "array",
            "description": "Line items when not copied from a job",
            "items": {
              "type": "object",
              "properties": {
                "description": {
                  "type": "string"
                },
                "qty": {
                  "type": "number",
                  "description": "default 1; for per-foot / per-sq-ft lines the size"
                },
                "unit": {
                  "type": "string",
                  "description": "ft, sq ft, hr, visit… or empty"
                },
                "unit_price": {
                  "type": "number",
                  "description": "Price per unit in the business currency"
                }
              },
              "required": [
                "description",
                "unit_price"
              ]
            }
          },
          "valid_days": {
            "type": "integer",
            "description": "How many days the quote is valid; omit = no expiry"
          },
          "deposit_pct": {
            "type": "number",
            "description": "Deposit required, percent"
          },
          "notes": {
            "type": "string",
            "description": "Note visible to the client"
          }
        }
      }
    },
    {
      "name": "create_invoice",
      "kind": "write",
      "permission": "invoices.write",
      "description": "Create an INVOICE as a draft document. Use for \"invoice X\", \"bill Nuno for yesterday's job\", \"выставь счёт\". Either name the JOB (its service lines are copied) or give the client and the line items with prices. Never invent a price — ask. Tax comes from the business settings and the due date from its payment terms; the card shows lines, tax and total before anything is created. To record money already received use add_payment, not this.",
      "parameters": {
        "type": "object",
        "properties": {
          "client_name": {
            "type": "string",
            "description": "Client name (match from data). Required unless job is given"
          },
          "job": {
            "type": "string",
            "description": "An existing job to invoice — its title or its client name; the job's service lines are copied"
          },
          "items": {
            "type": "array",
            "description": "Line items when not copied from a job",
            "items": {
              "type": "object",
              "properties": {
                "description": {
                  "type": "string"
                },
                "qty": {
                  "type": "number",
                  "description": "default 1; for per-foot / per-sq-ft lines the size"
                },
                "unit": {
                  "type": "string",
                  "description": "ft, sq ft, hr, visit… or empty"
                },
                "unit_price": {
                  "type": "number",
                  "description": "Price per unit in the business currency"
                }
              },
              "required": [
                "description",
                "unit_price"
              ]
            }
          },
          "due_days": {
            "type": "integer",
            "description": "Days until due; omit = business payment terms"
          },
          "notes": {
            "type": "string",
            "description": "Note visible to the client"
          }
        }
      }
    },
    {
      "name": "get_setup_gaps",
      "kind": "read",
      "permission": "settings.financial.read",
      "description": "What is missing or wrong in this business's setup — the tester's eyes. Call it when the owner asks what to improve, what is not set up, \"посмотри, что не так / что улучшить\", \"what am I missing\", \"is everything configured\". Returns concrete gaps (empty payment instructions, services with no price, tax off where the state charges it, no logo, no clients yet, drafts never sent, invoices unpaid past terms…) with where to fix each. Answer with the gaps most important first; propose fixes as actions the owner confirms — never change anything on your own.",
      "parameters": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "update_invoice",
      "kind": "write",
      "permission": "invoices.write",
      "description": "Change the DATES or NOTES of an EXISTING invoice: the issue date (\"поставь дату счёта 17 сентября\", \"the invoice should be dated the job day\"), the due date, or the notes. Name the invoice by its number (\"INV-2026-0196\") or by the client (\"last invoice for Joe\") — the card shows was → will be before anything changes. Only pass the fields being changed. NOT for line items, prices or tax (that is still the invoice page), not for recording money (add_payment), not for creating one (create_invoice). Paid and cancelled invoices cannot be changed.",
      "parameters": {
        "type": "object",
        "properties": {
          "invoice": {
            "type": "string",
            "description": "Which invoice — its number, or the client name for their latest unpaid invoice"
          },
          "issue_date": {
            "type": "string",
            "description": "New issue date, YYYY-MM-DD"
          },
          "due_date": {
            "type": "string",
            "description": "New due date, YYYY-MM-DD"
          },
          "notes": {
            "type": "string",
            "description": "New notes text (replaces the old notes)"
          }
        },
        "required": [
          "invoice"
        ]
      }
    },
    {
      "name": "create_proposal",
      "kind": "write",
      "permission": "quotes.write",
      "description": "Create a PROPOSAL — a quote with OPTIONS — as a draft document: two or three levels of the same job (budget / mid / premium), each with its own services and its own total, one marked recommended. Use when the owner asks for options, variants or packages (\"сделай предложение с тремя вариантами\", \"quote him three options\", \"give her a budget and a premium version\"). For ONE fixed scope use create_quote. Name every service EXACTLY as it is in get_price_list (call it first if you have not this conversation): the server takes each rate, unit and \"what's included\" list from the price list and multiplies per-foot rates by the client's size — never pass or compute a price. Write the intro and each option's summary honestly: what the option will and will NOT achieve. The card shows every option with its total before anything is created.",
      "parameters": {
        "type": "object",
        "properties": {
          "client_name": {
            "type": "string",
            "description": "Client name (match from data)"
          },
          "title": {
            "type": "string",
            "description": "Document title, e.g. \"Detailing Proposal — Hunter 33\"; omit for a default"
          },
          "intro": {
            "type": "string",
            "description": "One short paragraph for THIS client: what was seen or asked, and why these options"
          },
          "size": {
            "type": "number",
            "description": "Client size (boat length in ft) — only when the owner said it and it is not on the client card"
          },
          "options": {
            "type": "array",
            "description": "Cheapest first",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string",
                  "description": "Option name, e.g. \"Deep Wash + One-Step Compound + Wax\""
                },
                "level": {
                  "type": "string",
                  "enum": [
                    "budget",
                    "mid",
                    "premium"
                  ]
                },
                "recommended": {
                  "type": "boolean",
                  "description": "true for the ONE option you recommend"
                },
                "time": {
                  "type": "string",
                  "description": "How long it takes, in the owner's words: \"2 full days\""
                },
                "summary": {
                  "type": "string",
                  "description": "Honest paragraph: what this option does and what it will NOT do"
                },
                "outcome": {
                  "type": "string",
                  "description": "The result in a few words for the summary table: \"Better shine, may not be even\""
                },
                "services": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Service name exactly as in get_price_list"
                      },
                      "result": {
                        "type": "string",
                        "description": "What the client gets from this line, one short phrase"
                      },
                      "qty": {
                        "type": "number",
                        "description": "Hours / visits for a service priced per hour or per visit, when the owner said it. Never for per-foot services"
                      }
                    },
                    "required": [
                      "name"
                    ]
                  }
                }
              },
              "required": [
                "title",
                "services"
              ]
            }
          },
          "not_included": {
            "type": "array",
            "description": "What the price does not cover (materials, haul-out, marina fees…)",
            "items": {
              "type": "string"
            }
          },
          "terms": {
            "type": "array",
            "description": "Terms the owner stated (deposit, balance, access needed). Never invent amounts",
            "items": {
              "type": "string"
            }
          },
          "valid_days": {
            "type": "integer",
            "description": "How many days the proposal is valid; omit = no expiry"
          }
        },
        "required": [
          "client_name",
          "options"
        ]
      }
    }
  ],
  "known_missing": [
    "change the line items, prices or tax of an existing invoice (dates and notes: update_invoice; the rest is the invoice page)"
  ]
}
