Tool definitions
How client-executed tools are declared and round-tripped.
The relay does not run tools. It forwards the tools array from your agent streaming request to the model and streams back tool_calls; your client executes them locally and posts the results on the next leg. The tools you send define what the agent can do — the relay adds nothing. The Mel app sends the definitions on this page, gated by the request's tool_role; a third-party client can reuse them verbatim.
ToolDef shape
{
"name": "read_file",
"description": "Read a file from the workspace",
"parameters": { "type": "object", "required": ["path"],
"properties": { "path": { "type": "string" } } }
}parameters is a JSON Schema object describing the arguments.
The tool-call round-trip
- You POST a leg with
messagesandtools. - The relay streams a
tool_callsevent:{"calls": [{"id", "name", "arguments"}], "usage?": ...}—argumentsis a JSON-encoded string. - Your client executes each call locally (read the file, run the command, ...).
- You append the assistant message carrying the
tool_calls, then onerole:"tool"message per call whosetool_call_idequals the call'sidand whosecontentis the result text. - You POST the next leg with the extended message list. Repeat until the relay emits
doneinstead oftool_calls.
The tool_call_id pairing is what tells the model which result answers which call — never omit it, and never reorder ids across calls. Each leg is one paid provider call and one quota unit.
Base tools (every role)
{"name":"read_file", "parameters":{"type":"object","required":["path"],
"properties":{"path":{"type":"string"},
"offset":{"type":"integer"},
"limit":{"type":"integer"}}}}
{"name":"list_dir", "parameters":{"type":"object",
"properties":{"path":{"type":"string"}}}}
{"name":"grep", "parameters":{"type":"object","required":["pattern"],
"properties":{"pattern":{"type":"string"}}}}
{"name":"find_files", "parameters":{"type":"object","required":["pattern"],
"properties":{"pattern":{"type":"string"}}}}
{"name":"write_file", "parameters":{"type":"object","required":["path","content"],
"properties":{"path":{"type":"string"},"content":{"type":"string"}}}}
{"name":"edit_file", "parameters":{"type":"object","required":["path","old_string","new_string"],
"properties":{"path":{"type":"string"},"old_string":{"type":"string"},
"new_string":{"type":"string"},"replace_all":{"type":"boolean"}}}}
{"name":"run_command","parameters":{"type":"object","required":["command"],
"properties":{"command":{"type":"string"}}}}
{"name":"web_search", "parameters":{"type":"object","required":["query"],
"properties":{"query":{"type":"string"}}}}
{"name":"write_plan", "parameters":{"type":"object","required":["title","markdown"],
"properties":{"title":{"type":"string"},"markdown":{"type":"string"}}}}
{"name":"create_pdf", "parameters":{"type":"object","required":["content","output_path"],
"properties":{"content":{"type":"string"},"output_path":{"type":"string"},"title":{"type":"string"}}}}Notes: read_file truncated reads report the offset to resume from; grep and find_files are project-wide and take no path/directory argument; edit_file requires an exact, unique old_string match; run_command results come back as exit_code=N followed by combined stdout+stderr; create_pdf renders via the best local engine (Chrome/Edge/Chromium, then wkhtmltopdf/weasyprint) with a built-in renderer as the guaranteed floor, and reports which one produced the file.
Shared project memory tools
Workers and orchestrators also get remember/recall. The project is derived automatically from the working directory server-side — the model supplies no project or query argument (do not add one):
{"name":"remember","parameters":{"type":"object","required":["note"],
"properties":{"note":{"type":"string"}}}}
{"name":"recall", "parameters":{"type":"object","properties":{}}}These back onto the project memory store.
Orchestration tools
A lead agent driving parallel workers uses open_pane (a CLI agent such as claude or codex, or a shell command, in a pane) and spawn_agent (a Mel agent worker):
{"name":"open_pane", "parameters":{"type":"object","required":["layout"],
"properties":{"layout":{"type":"string","enum":["right","down","tab"]},
"command":{"type":"string"}}}}
{"name":"spawn_agent","parameters":{"type":"object","required":["layout","task"],
"properties":{"layout":{"type":"string","enum":["right","down","tab"]},
"model":{"type":"string"},
"task":{"type":"string"}}}}
{"name":"send_keys", "parameters":{"type":"object","required":["pane","text"],
"properties":{"pane":{"type":"integer"},"text":{"type":"string"}}}}
{"name":"send_agent","parameters":{"type":"object","required":["pane","text"],
"properties":{"pane":{"type":"integer"},"text":{"type":"string"}}}}
{"name":"read_pane", "parameters":{"type":"object","required":["pane"],
"properties":{"pane":{"type":"integer"}}}}
{"name":"read_agent","parameters":{"type":"object","required":["pane"],
"properties":{"pane":{"type":"integer"}}}}
{"name":"list_panes","parameters":{"type":"object","properties":{}}}The
layoutenum is load-bearing. Onopen_pane/spawn_agent,layoutmust be constrained toenum: ["right", "down", "tab"]. Without the enum, models emit invalid values and pane placement breaks. Constrain it in the schema; the Mel app additionally normalizes any unknown value to"tab"as a safety net.
Role gating
The Mel app sends the set matching the request's tool_role:
tool_role | Tools sent |
|---|---|
null (normal) | base tools only |
worker | base + recall, remember |
| orchestrator (CLI) | base + open_pane, send_keys, read_pane, list_panes, recall, remember |
| orchestrator (agent) | base minus write_file/edit_file (the lead delegates, doesn't author) + spawn_agent, send_agent, read_agent, list_panes, recall, remember |