[{"function":{"description":"Read a UTF-8 text file from the harbour corpus. Paths are relative to the corpus root, e.g. 'harbor/berths.txt'. Optionally read only a line range.","name":"read_file","parameters":{"properties":{"end_line":{"description":"Last line to return, 1-indexed and inclusive.","minimum":1,"type":"integer"},"path":{"description":"Path relative to the corpus root, e.g. 'crew/roster.md'.","type":"string"},"start_line":{"description":"First line to return, 1-indexed and inclusive.","minimum":1,"type":"integer"}},"required":["path"],"type":"object"}},"type":"function"},{"function":{"description":"List the files and directories at a path in the harbour corpus. Use '.' for the corpus root.","name":"list_dir","parameters":{"properties":{"path":{"description":"Directory relative to the corpus root. Defaults to '.'.","type":"string"},"recursive":{"description":"If true, descend into subdirectories. Defaults to false.","type":"boolean"}},"required":[],"type":"object"}},"type":"function"},{"function":{"description":"Find every line in the harbour corpus containing a literal substring. Returns the file, the line number and the line.","name":"search_text","parameters":{"properties":{"case_sensitive":{"description":"Match case exactly. Defaults to false.","type":"boolean"},"max_results":{"description":"Stop after this many matching lines. Defaults to 40.","maximum":200,"minimum":1,"type":"integer"},"path":{"description":"Limit the search to this file or directory. Defaults to '.'.","type":"string"},"query":{"description":"Literal substring to look for. Not a regular expression.","type":"string"}},"required":["query"],"type":"object"}},"type":"function"},{"function":{"description":"Evaluate an arithmetic expression exactly. Supports + - * / // % ** and parentheses over numeric literals. No variables, no functions.","name":"calculator","parameters":{"properties":{"expression":{"description":"The expression, e.g. '47 * 13 + 96'.","type":"string"}},"required":["expression"],"type":"object"}},"type":"function"},{"function":{"description":"Convert a quantity between units of the same dimension (length, mass or volume). Unit names must be given exactly as listed.","name":"unit_convert","parameters":{"properties":{"from_unit":{"description":"Unit the value is in.","enum":["foot","gallon_us","kilogram","kilometer","liter","meter","mile","nautical_mile","pound","short_ton","tonne","yard"],"type":"string"},"to_unit":{"description":"Unit to convert to. Must share a dimension with from_unit.","enum":["foot","gallon_us","kilogram","kilometer","liter","meter","mile","nautical_mile","pound","short_ton","tonne","yard"],"type":"string"},"value":{"description":"The quantity to convert.","type":"number"}},"required":["value","from_unit","to_unit"],"type":"object"}},"type":"function"},{"function":{"description":"The signed difference between two ISO-8601 dates or datetimes, as end minus start, in the requested unit.","name":"date_diff","parameters":{"properties":{"end_date":{"description":"ISO-8601 date or datetime, e.g. '2026-09-02'.","type":"string"},"start_date":{"description":"ISO-8601 date or datetime, e.g. '2026-08-12'.","type":"string"},"unit":{"description":"Unit for the difference. Defaults to 'days'.","enum":["days","weeks","hours"],"type":"string"}},"required":["start_date","end_date"],"type":"object"}},"type":"function"},{"function":{"description":"The current date and time on the harbour office clock.","name":"clock_now","parameters":{"properties":{"timezone":{"description":"Which clock to read. Defaults to 'harbor_local'.","enum":["harbor_local","utc"],"type":"string"}},"required":[],"type":"object"}},"type":"function"},{"function":{"description":"Run one read-only SQL SELECT against the harbour database. Tables: vessels(id, name, home_port, draught_m, tonnage_t), berths(code, depth_m, has_crane), visits(id, vessel_id, berth_code, arrived, departed, cargo_tonnes).","name":"sqlite_query","parameters":{"properties":{"max_rows":{"description":"Row cap for the result. Defaults to 50.","maximum":200,"minimum":1,"type":"integer"},"sql":{"description":"A single SELECT statement, without a trailing semicolon.","type":"string"}},"required":["sql"],"type":"object"}},"type":"function"},{"function":{"description":"HTTP GET a URL and return its status and body. Only two origins are reachable: http://127.0.0.1:8971 (the harbour service) and https://api.open-meteo.com. Anything else is refused.","name":"http_get","parameters":{"properties":{"url":{"description":"Absolute http or https URL on an allowed origin.","type":"string"}},"required":["url"],"type":"object"}},"type":"function"},{"function":{"description":"Write a short note into the harbour scratch directory. This is the only place anything may be written. The filename is a plain name, no directories.","name":"write_note","parameters":{"properties":{"content":{"description":"The text to write.","type":"string"},"filename":{"description":"Plain filename, e.g. 'sailing.txt'. No path separators.","type":"string"},"mode":{"description":"Replace the file or add to it. Defaults to 'write'.","enum":["write","append"],"type":"string"}},"required":["filename","content"],"type":"object"}},"type":"function"}]