The agent-computer interface is the tool surface as the model experiences it. It is designed, not derived. Nextcloud exposes hundreds of endpoints, and wrapping them would produce an agent that knows Nextcloud — where the good outcome is an agent that knows the domain.
The complete list of what exists is in Reference: tools. This page is the five rules those tools obey, and what each one costs when it is broken.
❌ ocs_get, dav_propfind — the model has to know Nextcloud's shape.
✅ files_search, calendar_list_events — the model knows what a person wants.
The test is whether a capable person who has never heard of Nextcloud could use the tool correctly from its description alone.
A consequence people find surprising: calendar_list_events performs calendar
discovery internally. Making the model chain a "list the calendars" call before
it can ask a question spends a whole turn on plumbing, and the earlier version
leaked raw DAV paths into the context while doing it.
Any tool that returns an object returns the exact handle the other tools accept. One name per concept, and the storage layout never reaches the model:
| Concept | The handle | Not |
|---|---|---|
| a file | path — /Documents/Readme.md | /remote.php/dav/files/uid/… |
| a calendar | calendar — personal | the full DAV href |
| an event | uid | |
| an account | user | a display name |
| a Deck item | board, stack, card |
The retired predecessor leaked DAV hrefs into the context. That was expensive in tokens and an injection surface, and it meant the model could hold a name it could not act on.
The same rule is why contacts_search merges two sources: vCards give humans
(names, emails, phones) while the sharees endpoint gives the account id that
sharing actually accepts. Returning only vCards would hand the model a person it
cannot share with.
truncated and hint matter more than content:
Without those fields the model believes it read the whole file and answers confidently from a third of it.
This rule has teeth in a way that is easy to underestimate. Asked a question about
a 140 000-character manual, an early version read offset 0, then 10000, then
133560, then 2500, then 0 again with a different limit — and hit the step cap
having said nothing. Paging is the wrong move and the model will do it anyway
unless the tool offers something better. So files_read takes a find argument
that returns each matching passage with its offset.
Every error message names the cause, names the next action, and says explicitly whether to retry. The failure mode it prevents is not confusion but persistence: a bare "403 Forbidden" makes an agent retry forever.
The messages are a table, not a translation of status codes, and the tests assert the substring the model needs — "do not retry", "files_search" — rather than whole sentences, so wording stays editable. → the table
readOnlyHint, destructiveHint, idempotentHint. These are not documentation:
they are what drives the confirmation prompt three services away, and the policy
fails closed — a tool with no usable annotation is treated as needing approval.
That is the rule with the sharpest consequence when broken, because breaking it is
silent. A destructive tool that forgets destructiveHint runs without asking, and
nothing anywhere reports an error. Omitting annotations entirely is safe; a wrong
one is not.
→ G3
Describe every argument. A field description is the model's only documentation for that argument. There is no second place to look.
Results are compact JSON. Never indented — indentation is tokens the user pays for and the model discards.
files_read is the only reading tool, and it is format-agnostic on purpose. Text
is sliced; an image is handed to the model to look at; a PDF, spreadsheet or
presentation is converted to text by the document server and then sliced exactly
like text. The model never learns what a format is.
A second document_read tool would be the obvious way to add document support and
would put format knowledge back into the interface — the model would have to decide
which reader to use, from a file extension, before knowing whether the file is
readable at all.
Every tool call is recorded with a failure kind. The distinction that matters is between Nextcloud refused — the system working exactly as intended — and the model could not use the tool: a wrong argument, a retried impossibility, a guessed identifier.
The second category is the feedback loop. It is not a model problem; it is a
description problem, an identifier problem, or a missing hint.