Laver
This MCP server provides tools to read and drive Laver kanban boards, tickets, and the workspace wiki via the public REST API.
Workspace & Board Management
List accessible workspaces and boards (
list_workspaces,list_boards).Get board details including status columns, labels, members, and tickets (
get_board).Create boards, optionally from
crmorsales-leadstemplates (create_board).
Tickets — Reading & Search
List/filter tickets on a board with paging and
updated_sincepolling (list_tickets).Get full ticket details including
versionfor optimistic locking (get_ticket).Read ticket comments and activity history (
get_ticket_comments).Search tickets, boards, wiki pages, and comments across a workspace (
search).
Tickets — Modifying
Create tickets with title, markdown description, status, priority, and due date (
create_ticket).Update ticket fields (requires
version); label/assignee/custom field replacement is wholesale (update_ticket).Move ticket to another status column (
move_ticket).Add markdown comments (
comment_on_ticket).Archive tickets to 30-day recoverable trash (
archive_ticket); permanently delete trashed tickets (delete_ticket).Link tickets as
blocks/blocked_by(link_tickets); remove all links between tickets (unlink_tickets).
Attachments
List attachment metadata (
list_ticket_attachments).Fetch attachment content: text/CSV inline, images as image blocks, large/binary via
save_topath (get_ticket_attachment).Upload attachments from a file path or inline text (
upload_ticket_attachment).Delete attachments (trash 30 days, no restore tool) (
delete_ticket_attachment).
Wiki
List wikis, including archived wikis (
list_wikis).Full-text search within a wiki (
search_wiki).Get page tree with titles, UUIDs, nesting (
get_wiki_tree).Read page content and historical versions (
get_wiki_page,get_wiki_page_version).Create pages with markdown content and optional parent nesting (
create_wiki_page).Append markdown to existing pages; add-only, no conflicts (
append_wiki_page).Restore an archived wiki and its pages (
restore_wiki).
Automations
List automation rules on a board (
list_automations).Create live automation rules with triggers (events, schedules, due dates), conditions, and up to 20 actions; goes live immediately (
create_automation).
Limitations
No direct access to admin/account functions: cannot manage users, workspaces, public links, exports, bulk operations, or restore deleted attachments/trash generally, except archived wikis can be restored via restore_wiki.
@laver/mcp
An MCP server for Laver. Gives an agent tools to read and drive kanban boards, tickets and the workspace wiki.
Every tool is a thin call to the same public REST API the web app uses. There is no local state, no cache, and no second implementation of anything — if Laver refuses a write, the refusal comes back verbatim, because an agent can act on "409, re-read and retry" and cannot act on "something went wrong".
Setup
Create a workspace-scoped API key in Laver under Admin → API keys. It acts as the person who created it, so it can do exactly what they can do and nothing more, and it can be revoked without touching their account.
{
"mcpServers": {
"laver": {
"command": "npx",
"args": ["-y", "@laver/mcp"],
"env": { "LAVER_API_KEY": "your key here" }
}
}
}LAVER_API_URL overrides the API host; it defaults to https://api.laver.app.
LAVER_API_KEY_FILE is an alternative to LAVER_API_KEY: a path to either a
file containing nothing but the key, or a .env-style file with a
LAVER_API_KEY=… line among others (an assignment line wins; quotes and an
export prefix are both fine). That is how the .mcp.json in this repo
registers the server without a secret in a tracked file.
A file with neither — no assignment line, and more than one token in it — yields no key at all, and you get the "key is not set" error. It used to send the whole file as the token, which is fine for a file holding one secret and is a leak for anything else.
LAVER_API_URL must be https, except for localhost.
Working in this repo
.mcp.json at the repo root registers this server for anyone who opens the
project, reading the key from the gitignored .env. Nothing to export.
It runs the published package, npx -y @laver/mcp, rather than the
mcp/server.js beside it. That is deliberate: pointing it at the local file
meant everyone here ran the one code path no user takes, and that is precisely
how 0.1.0 shipped with an entry point that never connected its transport when
started through bin — which is the only way a real client starts it. Running
what we publish means we meet what users meet.
If you are editing this server, that same choice will fool you: your changes do nothing until they are published. Point the client at the working copy while you work on it —
{ "command": "node", "args": ["mcp/server.js"] }— and put it back before you commit. npm run check and
frontend/tests/check-mcp-bin-entrypoint.mjs both run against the working copy
regardless, so the tests never depend on a publish.
A client only connects to MCP servers at startup. claude mcp add while a
session is already running does not retrofit the tools into that session — the
tool list was built before the server existed. Start a new session (or
reconnect from the client's MCP panel) and the tools appear.
Related MCP server: Plate MCP
Tools
Reading
Tool | What it gives you |
| Where to start when you have no uuids |
| The boards in a workspace |
| A board with its status columns, labels, members and tickets |
| Tickets on a board, filterable, paged — |
| Triage across every board at once — |
| One ticket in full, its subtasks, including its |
| Comments and activity history |
| How long the ticket has spent in each column |
| A board's custom field definitions — the uuids |
| Every label in the workspace, not only the ones already used on one board |
| Boards, tickets, wiki pages and comments across a whole workspace at once |
list_workspace_tickets is the stand-up read: it needs no board_uuid, and it
is the only thing here that answers "what is late" and "what does nobody own"
without walking every board. It is deliberately the small sibling of
list_tickets — one page, 50 by default and 200 at most, no cursor — so narrow
it rather than paging it.
get_ticket_flow derives its numbers from the moves already in a ticket's
history. Read visits rather than the by_status totals if you are adding
several tickets up: tickets worked in one batch overlap, and their totals do not.
gaps says when the history and the ticket's current column disagree, which
makes the totals a floor rather than a measurement.
To follow a board, call list_tickets again with updated_since set to the
server_time the previous call returned; you get back the tickets that changed
and nothing else. There is no tool for the server-sent event stream at
GET /boards/:uuid/events — a tool call is one request and one answer, and a
stream that never ends is neither.
Asking for less. A tool result is charged against the model's context, and
three of these calls are the ones you cannot route around: a status_uuid or a
label_uuid comes from get_board, and a page_uuid comes from
get_wiki_tree. On a busy board they were 154 kB, 119 kB and 180 kB. Each now
takes a parameter that narrows the reply, and every one of them is opt-in — a
call that passes none of them is unchanged.
Call | Gives you | Measured |
| the structure alone — statuses, labels, fields | 155 kB → 17 kB |
| the top level, nothing nested under it | 119 kB → 1.8 kB |
| one page and everything beneath it | 119 kB → 9.8 kB |
| titles and uuids without the bodies | 167 kB → 45 kB |
Reach for get_board(include_tasks: false) whenever you called it for a uuid
rather than for the tickets, and list_tickets when you want the tickets —
that one takes status and limit as well.
Writing
Tool | Notes |
|
|
| Needs |
| Needs |
| Markdown in |
| Your own comments only; replaces the whole body and is marked as edited |
| Your own comments only; to the trash, and nothing here restores one |
| Clears this ticket's unread badge for the user the key acts as |
| One checklist item, appended — plain text, not markdown |
| Tick it off ( |
| Not recoverable — a checklist item has no trash |
| To the trash — recoverable for 30 days |
| Destroys one already in the trash — permanent |
| Optionally from a template — |
| "this before that" — direction is |
| From either end, and removes every kind of link on the pair |
Comments are not versioned, so none of the three comment writes takes a
version and none of them can 409 — the last edit wins. Only the author may
edit or delete one, and somebody else's is a 404 indistinguishable from a
comment that does not exist, so these never report who wrote what.
get_ticket_comments deliberately marks nothing read; mark_comments_read is
the only call that does, and it marks up to the newest comment that exists at
that moment rather than subscribing.
Subtasks are the checklist on a ticket — a progress count on the card, and where
acceptance criteria belong when they are meant to be ticked off one at a time.
get_ticket returns the items themselves, so there is no separate list tool.
Board structure
Tool | Notes |
| A new column, appended to the right-hand end |
| Rename, recolour, or set |
| The complete list of uuids, in order; a stale list is a 409 |
| The column must be empty, and a board keeps one |
| A swimlane; there is no rename route, so a wrong name is deleted and remade |
| Same complete-list contract as |
| Tickets in it survive, ungrouped — and their versions all move |
| Workspace owner or admin; |
| Rename, reorder, or replace a select's whole |
| Takes every ticket's value in that field with it — no trash, no restore |
This is what a board an agent creates needs to stop being its template's
defaults. The two reorder tools want the whole list because a partial one is how
two simultaneous reorders silently drop a column: read the uuids off get_board
immediately before calling, and re-read on a 409.
Labels
Tool | Notes |
| Workspace-wide, alphabetical — |
| A hex colour is required; names are unique ignoring case and space |
| Renames it everywhere — one label, not a copy per board |
| Removes it from every ticket that carries it |
A label is a workspace object shared by every board, which is the thing to be
sure of before renaming one: it changes for everybody. The uuids these return
are what update_ticket takes as label_uuids.
Attachments
Tool | Notes |
| Name, type, size and uuid — never the bytes |
| Text inline, an image as an image block, anything else via |
|
|
| To the trash for 30 days; there is no restore tool |
get_ticket reports attachment_total, so you know whether listing is worth a
round trip.
Binary content crosses the tool boundary by not crossing it. A tool result
is text, Laver allows 25 MB per file, and 25 MB of base64 is roughly nine
million tokens — so only text, CSV and images under 4 MB come back inline, and
everything else needs save_to, which writes the file to a path on the machine
running this server (normally the agent's own, since the client starts it as a
subprocess). Uploads go the same way round: file_path reads from that machine
and costs no context.
An image comes back as an MCP image block rather than as text, which is the only form a model can actually look at — that is the whole point of the tool, since the screenshot somebody attached is usually the specification.
Wiki
list_wikis, search_wiki, get_wiki_tree, get_wiki_page,
get_wiki_page_version, append_wiki_page, update_wiki_page,
create_wiki_page — which takes the body as markdown and nests under
parent_page_uuid — and restore_wiki.
Archiving a wiki (done in the browser; there is no tool here for it) takes it
and every page under it out of list_wikis entirely. list_wikis takes
archived: true to see those instead — the only place an archived
wiki_uuid is visible at all — and restore_wiki is the only thing here
that does something with one: it un-archives the wiki, and every page under
it, in one call.
get_wiki_page_version is how you read something that was overwritten. It is a
read: the page does not move. There is deliberately no tool to put an old
version back — read it and write the wording you want with update_wiki_page,
which leaves a version of its own behind.
append_wiki_page adds to the end of a page and update_wiki_page replaces
what is on one; prefer the first when you are adding, because it takes no
version and two agents appending at once both get their text. Replacing takes
the version from get_wiki_page, and a page somebody wrote to meanwhile is a
409 carrying the current version rather than an overwrite — the guard that lets
this exist at all, since wiki pages sit behind a live collaborative editor. A
replace re-seeds that editor from what it just wrote, so a colleague with the
page open sees the new text instead of putting the old text back, and every
previous save stays readable through get_wiki_page_version. There is still no
tool to delete a page.
The markdown goes through the same parser ticket descriptions do. Headings,
lists, tables, code blocks, blockquotes, rules and links survive; so does an
 image, as a reference to that URL — there is no tool here to
upload an attachment, so the URL has to be public already. Raw HTML is kept as
literal text rather than interpreted.
Automations
Tool | Notes |
| The rules on a board, each with its |
| One rule in full — the conditions and actions an edit replaces |
| What a rule has actually done, newest first |
| Owner or admin only — and see below before calling it |
| Needs |
| Needs |
An automation rule is a trigger, optional conditions and up to twenty actions, stored against a board. Three things about them are worth knowing before an agent touches these tools:
A rule created here is live immediately. It fires on its trigger within a couple of seconds, so read
list_automation_runsafterwards rather than creating one speculatively to see what it would do.A rule is a standing grant. It runs as the user this key acts as, every time it is triggered, for as long as it exists — not once, like every other write in this server. Revoking the key does not stop it; disabling or deleting the rule does.
Pause before you delete.
update_automationwithenabled: falsestops a misbehaving rule at once and keeps its history; deleting destroys the history along with the rule.conditionsandactionsreplace the stored lists rather than merging into them, so build them fromget_automationand not from memory.
Both writes take the rule's version and answer 409 with the current one, the
same contract ticket writes have.
Published links
list_published_links is the inventory read: what of this workspace's is on the
public internet right now, each row with its share token, who published it, when,
and how many strangers have looked. live is what a stranger actually gets and
dark_reason says why a row is not, which is the only way to find a published
page that is currently archived and would go straight back online if somebody
restored it. Workspace owner or admin only; an ordinary member is a 403. Taking
a link back down is deliberately not a tool — report what is published and let a
person decide what comes off.
Not covered
The REST API is larger than this server, and the difference is deliberate rather
than accidental — mcp/route-coverage.js lists every backend route with either
the tool that calls it or the reason it has none, and npm run check fails if a
route appears that is in neither. That is what keeps this section true: it went
stale before, silently, which is how the server spent its whole life unable to
read a ticket's attachments while every check stayed green.
Not yet — wanted, not built:
Notifications. Still the biggest gap, and now blocked on something a tool cannot fix: an agent cannot see that it was mentioned or assigned, and
backend/notifications/index.jsinstallssession_onlyon the whole plugin, so every route there answers a key with a 401 however good the tool is. Adding one means first deciding whether a key may read its owner's inbox at all — a surface carrying other people's messages — which is a product and security call rather than a delivery task.list_workspace_ticketscovers the triage half and is the closest substitute meanwhile.Listing subtasks on their own. Needs no tool rather than lacking one:
get_ticketalready returns the checklist items themselves — uuid, title and done state — so a dedicated list route would be a second way to ask the same question. Writing them is covered.Board templates. A workspace can save one of its own boards as a template and start the next board from it. Listing them has no tool, which is what keeps the saved ones out of reach:
create_boardnames the two built-in ids in its schema, and a saved template's id is a uuid nothing here can discover. Saving and deleting are deliberately absent rather than pending — a template is workspace-wide furniture in everyone's board-create form, and adding to or removing from that list is a decision taken in front of the board.Attachment thumbnails. Deliberate, not a gap. The thumbnail route serves the web client a downscaled webp so a card cover costs kilobytes instead of megabytes; an agent wants the file somebody actually uploaded, and
get_ticket_attachmentalready returns that at full resolution in its original format.Where you left off. The five things the ⌘K palette offers a person before they type anything — the tickets and pages they last opened or worked on. Work done through a key is kept out of it on purpose, so a tool here would ask which tickets its owner had been reading;
list_workspace_ticketsis the better answer to what to pick up, andsearchto anything more specific.Board analytics. A small loss now that the triage half is covered by
list_workspace_tickets. What is left is shaped for charts rather than for a decision: stats and board-wide flow are aggregates a person reads on a screen, activity is a feed, search-text serves find-as-you-type, and export hands back a file.get_ticket_flowcovers the one figure an agent acts on, per ticket, where it can be attributed.The wiki page a board is about. Deliberate, because every part of the answer is already reachable and the route is only the join.
get_boardreturns the board'swiki_page_uuid, andget_wiki_pageandget_wiki_treeread that page and everything under it with the same access rules and more of the content. The route exists so a person looking at a board can reach the handbook without remembering its name; an agent holding the uuid needs no such shortcut.A conductor's scoreboard. Never, and not for want of the tool being easy. The route reads back how the tickets one person wrote fared once an agent picked them up, for that person alone; an agent reading the scores of the people briefing it is the wrong way round, and one that could read them could play to them.
Pressing an automation button. Deliberate rather than pending: a
manualrule exists so that a person decides when it runs, and a tool that pressed it would hand that back. Creating one is the safe half and is covered.Ticket history, duplication and recurrence.
Sprints. Blocked in the same place notifications are:
backend/sprints/index.jsinstallssession_onlyon the plugin, so a key gets a 401 from every route there —POST /boards/:uuid/sprintsincluded. Opening them to keys is a decision about what a key may do to a team's planning cadence (a rollover migrates every unfinished ticket onto a new board), not a matter of writing the tools.Deleting and rearranging a wiki page. Editing one is covered now, by
update_wiki_page; archiving, moving and duplicating are not, because they change what a colleague can find rather than what a page says, and a page nobody can find has no version history to consult. Removing a whole wiki (DELETE /wikis/:wiki_uuid, which archives it and every page under it) is in the same group: it takes a wiki's published pages off the internet in the same instant it archives them, which is consent, not editing. Its restore route is covered instead, byrestore_wiki, now thatlist_wikiscan find an archived uuid to give it.Commenting on a wiki page. Uncovered because of the anchor rather than the shape of the call. A comment there is attached to the words it is about — the quote, and which occurrence of it — because a page is edited collaboratively and a stored document position comes to mean different text the moment a colleague types above it; the client re-finds that quote in the document it has just rendered. An agent holds no document, so it would be sending a quote it believes is on the page, and the failure it hits most is silent: a phrase that appears twice, counted differently at the two ends, anchors the remark to the wrong sentence. A tool worth having would take the page and the quote and say plainly when the quote was not found, which is the right next job if agents are ever asked to review pages. Until then a key already has the honest way to say something about a page:
append_wiki_pageandupdate_wiki_pageput it IN the page, where the people reading it will see it, rather than in a margin no other tool can read back. Editing, withdrawing and resolving follow from that — there is nothing for a key to edit while it cannot comment — and resolving in particular takes the highlight off somebody else's prose and declares a conversation between people finished. Reading the thread is uncovered on its own merits: those comments are a discussion about a draft, and an agent asking what a page says wantsget_wiki_page.Imports and feedback forms.
Not ever, from a key:
Reporting a deployment.
POST /tasks/:task_uuid/deploymentsis how a build pipeline tells Laver that a ticket's pull request reached an environment or failed to, so the people assigned to it are told. The caller is a CI job holding a key in a secret, at a moment when no model is running; a tool over it would let an assistant announce a deployment no pipeline performed, and the whole worth of the notification is that it reports a fact rather than a claim. The reading half an agent might want it already has: the deployment is a ticket event, soget_ticket_flowand the timeline carry what happened, when, and which key said so.Reporting a merged pull request.
POST /tasks/:task_uuid/pull-requests/mergedis the same bargain: a CI job says the ticket's pull request was merged, and the board moves the ticket into the column tagged for merges. A tool would be a second way to move a card — an agent hasmove_ticket— differing only in that it also writes "this ticket's pull request was merged" onto the timeline, which an assistant that merged nothing has no business claiming. Reading is covered: the merge is a ticket event, soget_ticket_flowcarries it.Public links and publishing. Publishing turns something private into something anyone with the URL can read. That is consent, and a tool call is the wrong shape for it.
Taking a published link back down. The read shipped and the two DELETEs did not, which is the same split: a tool cannot carry consent, and that says nothing about ASKING what is public.
list_published_linksanswers the question and grants no power the caller did not already have, since every link it names is public by definition. Retracting one is a bounded admin power exercised in front of a screen showing what is about to go dark, so an agent reports the inventory and a person decides what comes down.Asking a person for their signature. Refused at the route rather than merely unbuilt: all three answer a key with a 401 before they read anything. Sending a request puts a message into a stranger's inbox carrying our SPF and DKIM, and the judgement that an address deserves to be asked for a signature is a person's — a key acts as whoever minted it and would inherit their edit access, so permissions would not stand in the way. Listing the outstanding requests is the dialog's own read, returning them beside the sections computed from the live document they may attach to, and
get_wiki_pagealready carries the text. Withdrawing one is the same judgement from the other side.Taking a board away, and how it looks. The structure half of this group is covered — statuses, groups and custom fields all have tools. What is left is removal and decoration. Archiving and deleting a board are one write under two names, and a tool over either would let a key take a whole board and every ticket on it out of the workspace's view in one call; restore is uncovered as a consequence, since nothing an agent can do archives a board and
list_boardsdeliberately cannot find an archived uuid. Permanent deletion is firmer still — the row, its tickets and their attachments' bytes all go — and is a person's decision made twice, from a screen showing what they are about to lose. The board's colour and background picture are decoration chosen while looking at the board, which is the one thing an agent cannot do. A column's entry requirements sit here too: the read needs no tool, becauseget_boardalready sends each column'sentry_requirementsand a refusedmove_ticketnames every one the ticket does not meet — but a key that could set them could remove them and then move the ticket, so the gate would only be as strong as the weakest tool over it.Moving a board between workspaces. Not a judgement about blast radius — a key cannot make this call at all. A key is confined to the one workspace it was issued for, and
POST /boards/:board_uuid/move-workspacenames two: the workspace the board is in, and the one it is going to. A tool over it would need a key that reached both, which is the confinement gone, or it would refuse every call it was ever given. The gesture is not an agent's either: an admin of two workspaces decides which of them a whole board — its tickets, comments, attachments and history — belongs to, and the route requires owner or admin in both ends before it moves anything.Workspace and membership administration. A key acts as the person who created it; renaming or deleting their workspace, or answering an invitation for them, reaches further than delegating a board task ever meant.
The whole-workspace export.
POST /workspaces/:uuid/exportsand the routes beside it build one file holding every ticket, comment and wiki page in the workspace, and they refuse a key before looking at anything else. A key in a CI variable that could ask for one turns any leak of it into a full data breach rather than the scoped access it was issued for. Taking a copy of the company's data is a thing a person does, signed in, from Admin, and the audit trail records which person.Bulk ticket writes.
POST /tasks/archivebins a list in one call.archive_ticket, one at a time, is the deliberate choice.Trash. One-way on purpose: an agent can archive and can destroy what it already archived, and a person puts things back.
The board event stream. Server-sent events; a tool call is one request and one answer.
list_ticketswithupdated_sinceis the replacement.Scheduler endpoints. The deployment's own cron hooks.
Removing a ticket
Two steps, deliberately, so that nothing is destroyed by a single call:
archive_ticket task_uuid → the workspace trash, recoverable for 30 days
delete_ticket workspace_uuid + task_uuid → gone, and nothing brings it backdelete_ticket refuses anything that is not already archived, so the order is
enforced by the server rather than by convention. There is no restore tool here
— a ticket in the trash is put back from the web app — so treat
archive_ticket as the furthest you can go on your own.
Read the ticket before you archive it if you intend to destroy it:
delete_ticket needs the workspace_uuid, get_ticket is where you get one,
and an archived ticket can no longer be read.
Working out what to do next
Every ticket read carries blocked_by, blocks and is_blocked. is_blocked
is false once every blocker has reached a completion column, so the tickets a
board is ready for are the ones where it is false. link_tickets records the
dependency; a link that would make a loop is refused with a 409, because a loop
makes the ordering unanswerable.
The one rule worth knowing
Tickets carry a version. Every write must send the version you read, and a
write against a stale one is refused with 409 rather than silently
overwriting whoever got there first. The server turns that into an instruction,
and Laver's refusal carries the current version, so the instruction can include
it rather than spending a second call on it:
Laver 409: Task was updated by another request.
Somebody wrote first, so the version you sent is stale. The current version is 12. If your change does not depend on what you read — moving a ticket to a named column, say — retry with that version. If it does, call get_ticket again and decide against the ticket as it now is, or you will quietly undo the other write.
Read, then write. Do not cache a version across a long turn.
When the key is refused
A 401 is the key: missing, mistyped, revoked, expired, or a placeholder that was never filled in. The underlying message is not always a fair description of what happened — a key the JWT layer cannot parse comes back as "Authorization token is invalid: The token is malformed", which sounds like a corrupted string when the usual cause is simply a key that was replaced. The server appends what to do about it, including the part that catches people out:
An MCP client reads that environment once, when it starts this server, so it must be restarted afterwards — editing the config in a running session changes nothing.
A 403 is different and is never worth retrying: the key was accepted, and then refused this particular action. It is scoped to another workspace, or the person it acts as has a read-only role, or is a guest without access to that board.
The key itself is read in exactly one place, sent as a bearer token, and never logged, echoed, or included in any error text.
Checking it
node check.js # schema, then every read-only tool actually called
node check.js --require-live # …and a skipped sweep is a failure, for CI
# the same calls against the API the published package actually talks to
LAVER_API_KEY_FILE=../.env node check.js --live-apiTwo halves. The first is static: every tool registered, classified read or
write, described, and given a schema. The second boots the backend from
../backend on a spare port, creates a workspace of its own, mints a key
against it, and calls every read-only tool — through the tool's own zod
schema and then its handler — sending every parameter the tool declares.
That half exists because the first one passed while list_wikis sent
?workspace= at a route that requires workspace_uuid. It 400'd on every call
it ever made, and since it is the only tool that yields a wiki_uuid, the whole
wiki half of this server was unreachable from the day it shipped — with
registration, descriptions and schema shape perfect throughout.
It needs the same things npm test in backend/ needs: that directory, its
node_modules, its .env, and the Postgres they point at. No API key and no
network beyond localhost — a real LAVER_API_KEY in the environment is ignored.
Without a backend it prints a banner saying the tools were not called and
runs the schema half alone; --require-live turns that into a failure.
It is only as local as backend/.env is, though. Running it writes to
whatever database that file points at: it creates a workspace, a board, two
tickets, a comment, a wiki, a page and an API key, and deletes them again at the
end. It also loads the backend into its own process. It listens with
app.server.listen rather than app.listen — the same idiom the collab and
board-events integration tests use — so Fastify's onListen hooks do not fire
and none of the seven schedulers start; without that, the billing sweep alone
would run against every workspace in that database. Point backend/.env at
staging and this is a check that writes to staging.
The sweep is read-only and stays that way: a check that creates tickets in
somebody's workspace every time it runs is a check people stop running. The
write tools are covered by the schema half only — see the note at the foot of
check.js for the way to cover them without sending a write.
Ctrl-C is safe. The sweep stops after the call in flight and the fixture workspace is deleted before the process exits; a second Ctrl-C kills it outright if the call in flight is the thing that is stuck.
Against the deployed API
--live-api points the same calls at LAVER_API_URL — https://api.laver.app
unless you say otherwise — with a real key, taken from LAVER_API_KEY or from
the file LAVER_API_KEY_FILE names, exactly as the server itself takes it.
It exists because a green local run and a working published package are two different claims. The local sweep proves the tools agree with the code in front of you; this package talks to the deployed API, so a route that ships a rename before the package does breaks every agent in the field while the local sweep stays green. That is a narrow window — the tools and the routes live in one repo and move together — but it is exactly the window publishing to npm opens.
Both modes run the same table, in cases.js, and every case carries an
expectation for each: exact counts locally, where the fixture is known, and
shapes and invariants live, where the workspace is somebody's real one and
cannot be seeded or torn down. A case with only one of the two is a failure, so
a new call cannot cover one transport and skip the other.
It creates nothing and deletes nothing, and that is enforced rather than
promised: live mode replaces fetch with one that refuses any method but GET,
so a write tool called by mistake cannot reach the network at all.
frontend/tests/check-mcp-live-api-mode.mjs runs the whole mode against a stub
API and asserts that every request that left the process was a GET.
Instead of a fixture it goes looking for something to point at, and wants a board with at least two tickets in at least two columns, in a workspace with a wiki that has a page. It refuses to run against anything thinner rather than passing quietly: a filter case against an empty board passes whether or not the filter was applied, which is the failure this whole file exists to prevent.
Opt-in, and never part of npm run check:all or CI — it needs a key and a
network, and neither belongs in a check that runs on a box with no secrets.
Publishing
0.1.0 is published and is broken. Do not tell anyone to install it. It
starts, registers all 20 tools, connects no transport, and exits 0 without
writing anything to stdout or stderr — so a client sees the process end and
nothing else. The entry-point guard compared the basename of process.argv[1]
against this file's name, which is true only for node mcp/server.js; npm's
shim for bin makes argv[1] node_modules/.bin/laver-mcp, so npx -y @laver/mcp — the way this README tells everyone to run it — never matched.
Fixed in 0.1.1, and tests/check-mcp-bin-entrypoint.mjs now spawns the server
through a symlink and speaks MCP to it, so the same class of bug cannot ship
again.
When 0.1.1 goes out, mark the broken one so nobody lands on it:
npm deprecate @laver/mcp@0.1.0 "Never connects its stdio transport when run via npx or the bin shim. Use 0.1.1 or later."Unpublishing 0.1.0 is the other option and is worse: within 72 hours it removes the version, but the number stays burned either way, and anything that already pinned it breaks rather than being warned.
The publish itself is the owner's to run, because it is public, permanent enough to matter, and takes a name nobody else can then have.
The name. laver on npm is taken — v1.0.0, published in 2021 by an
unrelated maintainer — so the bare name is not available and never will be.
This package is therefore @laver/mcp: the brand name kept as the scope,
with the generic part where it belongs. Checked against the registry on
6 Aug 2026 — @laver/mcp is free, and nothing has ever been published under the
old unscoped laver-mcp, so the rename costs nothing.
The scope exists and the first publish has happened. 0.1.0 and 0.1.1 are
on the registry under @laver/mcp, created 2026-08-06T22:14Z — which is the
only proof that matters that the scope resolves and the publishing account may
write to it. This paragraph used to say the scope did not exist yet; that was
true when it was written and is not now.
Do not re-test it with https://registry.npmjs.org/-/org/laver. That URL is a
404 unauthenticated whether the org exists or not, so it cannot tell the two
apart — read the package document instead:
curl -s 'https://registry.npmjs.org/@laver%2Fmcp' | python3 -m json.toolFor a self-hosted fork publishing under its own scope, the first publish still
needs that scope created at https://www.npmjs.com/org/create (free for public
packages) or confirmed as the account's own username, with npm whoami to
check membership. npm publish fails with
404 Not Found - PUT https://registry.npmjs.org/@<scope>%2fmcp if the scope
does not exist, which reads like a network fault rather than a missing org.
The executable stays laver-mcp. The package is @laver/mcp, but bin is
deliberately not renamed to mcp: a global install would put a command called
mcp on the PATH, which is far too generic and collides with every other MCP
server anyone installs. npx -y @laver/mcp works regardless — npx runs the
package's only bin whatever it is called — so nothing in the config snippet
above depends on the command's name.
repository and bugs point at the public mirror. They were absent while
github.com/Developyn/laver was the only home — it is private, npm renders both
fields as links on the package page, and aiming the only two "where does this
come from" links at a 404 is worse than having neither. Since August 2026 the
published files are mirrored to github.com/Developyn/laver-mcp, which is
public, so both now resolve. Note there is no "directory" key: the mirror's
root is the package, whereas here the same files live under mcp/.
The mirror is what every MCP directory anchors a listing to, so it has to keep
up. After each publish, copy the published tarball's contents over it —
npm pack @laver/mcp && tar xzf laver-mcp-<version>.tgz — and commit. Its
Dockerfile, glama.json and CI workflow are mirror-only and are not in
files, so they never ship to npm.
mcpName is for the official MCP registry, which matches the package
against the server name being published there. It must equal the namespace
mcp-publisher login github actually grants you — io.github.developyn/… if it
authorises the org, io.github.melvyn-developyn/… if only the personal account.
Getting it wrong is not fatal, but correcting it costs another version.
Before each one
2FA on the publishing account, if it is set to require it for publishing (npm enforces this for some accounts and packages and prompts for others). Passing
--otpsaves a prompt from failing a non-interactive run; drop it if not enrolled.npm whoamianswering with that account —npm loginif not.For CI instead of a laptop: an automation token in
NPM_TOKEN(granular, write-scoped to this package). Automation tokens bypass the 2FA prompt, which classic read-write tokens do not.A version the registry does not already have. npm refuses to republish an existing one, so this is not tidying — it is what makes a publish possible at all. Check what is live first, because the repo's number and the registry's can be equal while the contents differ, and nothing warns you:
npm view @laver/mcp version. Minor for new tools, patch for fixes to existing ones.
The publish
cd mcp
npm ci # the lockfile, not whatever resolves today
npm run check # schema + every read-only tool actually called
npm pack --dry-run # confirm the file list is LICENSE, README.md, package.json, server.js
npm publish --access public --otp=<code-from-your-authenticator>
npm cideletes and reinstallsmcp/node_modules. In the shared development checkout that directory is shared with every agent running against it, and removing it mid-run breaks their tests — which is why the agent instructions forbid it and why an agent preparing a release stops before this block. It is correct and expected for whoever actually publishes; just do not run it while others are working in the same tree.
--access public is required here: scoped packages default to restricted,
and a restricted publish on a free account is refused outright. publishConfig
in package.json already sets it, so the flag is belt and braces rather than the
only thing standing between this and a private package.
Afterwards
npx -y @laver/mcp # should start and wait on stdio, not exit
npm view @laver/mcpA mistake is recoverable only briefly: npm unpublish @laver/mcp@<version>
works within 72 hours, and the version number is burned afterwards regardless.
The package name is not returned to the pool by unpublishing a version.
Licence
MIT — see LICENSE, which ships in the package.
Available Tools
30 toolsappend_wiki_pageA
Add markdown to the END of a page that already exists. This is the tool for writing what you learned into your own section of a shared page — the thing create_wiki_page cannot do without leaving you a second page of the same name. It ADDS ONLY: it cannot change or remove a word that is already on the page, which is exactly why it is safe to offer where a general edit is not. There is still no tool to edit or delete existing content; if you need to correct something you appended, append the correction. Takes NO version and never conflicts — two agents appending to the same page at the same moment both get their text, in whichever order the server serialises them, and neither is asked to retry. Markdown is converted server-side by the same parser create_wiki_page uses, so headings, lists, tables, code blocks and links all survive; lead with a heading if you want your section to be findable. The reply is the page's identity and its new version, deliberately NOT the page body — appending does not need you to have read the page, and getting the whole document back is the cost this tool exists to avoid. Markdown that is only whitespace is a 400 rather than a version bump for no change. Needs write access to the workspace: a read-only role or a guest key is a 403 and retrying cannot fix it.
| Name | Required | Description | Default |
|---|---|---|---|
| page_uuid | Yes | From get_wiki_tree or a search_wiki hit. A wiki uuid passed here is a 404, not a page | |
| content_markdown | Yes | Appended after everything already on the page. Longer than 100k characters is refused rather than truncated |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full behavioral burden and does so comprehensively. It discloses the append-only safety model, concurrency behavior ('Takes NO version and never conflicts'), response shape ('deliberately NOT the page body'), whitespace-only edge case (400), and write-access requirements (403 for read-only/guest). No contradicting annotations exist.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place: purpose, safety, concurrency, formatting, response, error handling, and permissions. It is front-loaded with the core action and then layers necessary behavioral context. No redundant or filler sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no annotations and no output schema, the description fully equips an agent to invoke the tool correctly and understand consequences. It covers when to use, key behaviors, return value, error conditions, and auth requirements, making it complete for a mutation tool of this complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema covers both parameters at 100% with detailed descriptions, so baseline is 3. The description adds extra value for content_markdown by explaining server-side markdown rendering ('same parser create_wiki_page uses') and advising to 'lead with a heading' for findability. It doesn't add much for page_uuid, but the schema already handles that.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and scope: 'Add markdown to the END of a page that already exists.' It clearly distinguishes itself from the sibling tool create_wiki_page ('the thing create_wiki_page cannot do without leaving you a second page of the same name') and further clarifies its append-only nature ('cannot change or remove a word').
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use the tool ('This is the tool for writing what you learned into your own section of a shared page') and contrasts with create_wiki_page. It also provides guidance for corrections ('if you need to correct something you appended, append the correction') and notes that no edit/delete tool exists, which prevents misselection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
archive_ticketA
Move a ticket to the workspace trash — the recoverable half of deleting. It leaves the board, stops appearing in list_tickets and search, and get_ticket answers 404 for it, but it can be restored for 30 days from the workspace's trash in the web app, and is destroyed automatically once that window passes. This server has no restore tool, so from here archiving is a door only a person can reopen. It takes no version and cannot 409 — archiving is not a field edit, so there is nothing to conflict with. Already archived, never existed, and on a board you cannot open are all the same 404, so a retry is safe and a 404 tells you nothing about tickets you cannot see. If you intend to destroy the ticket permanently, call get_ticket FIRST and keep its workspace_uuid: delete_ticket needs one and an archived ticket can no longer be read to find it.
| Name | Required | Description | Default |
|---|---|---|---|
| task_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully carries the behavioral transparency burden. It discloses that archived tickets leave the board, disappear from list_tickets/search, return 404 from get_ticket, can be restored for 30 days, are auto-destroyed after that, and that 404 is ambiguous (already archived, never existed, or inaccessible). It also explains that no version parameter is accepted and that a retry is safe because there is no conflict. This is exceptional transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the primary purpose in the first clause, then methodically expands into effects, lifecycle, security, and prereqs for the sibling delete_ticket. While long, every sentence adds distinct value—no fluff or repetition. The structure flows from action → consequences → nuance → guidance, which is optimal for an AI agent needing to make safe decisions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite there being no output schema and no annotations, the description is remarkably complete for the tool's complexity. It covers the effect on all relevant ticket operations, the restoration window, the lack of a restore endpoint, the 404 ambiguity, and the prerequisite for delete_ticket. Given the tool's dangerous and nuanced behavior, this fully equips an agent to invoke it correctly and avoid irreversible mistakes.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It clarifies that only task_uuid is needed and explicitly states there is no 'version' parameter, which is a semantic detail beyond the schema. It also explains 404 semantics related to task_uuid (already archived, never existed, or on an inaccessible board), giving the agent deeper understanding of how the parameter value is interpreted. This far exceeds the minimal schema information.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Move a ticket to the workspace trash — the recoverable half of deleting.' This immediately clarifies the action and differentiates it from permanent deletion (delete_ticket). It also conveys the key consequence (leaves board, hidden from list/search, 404 on get_ticket), which distinguishes it from sibling tickets tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly frames archiving as the 'recoverable half of deleting' and warns that there is no restore tool on this server, making the human-web-app restoration the only path back. It then provides a concrete alternative: 'If you intend to destroy the ticket permanently, call get_ticket FIRST and keep its workspace_uuid: delete_ticket needs one...' This gives clear when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
comment_on_ticketA
Add a comment to a ticket. Markdown in body is parsed — headings, lists and code fences all render. Comments are not versioned: this takes no version and cannot 409, so it is the one write that is always safe to make against a ticket somebody else is editing, and it is the right way to report something rather than editing the description out from under them. It is also the write a read-only key can still make — a commenter role is refused update_ticket and move_ticket with a 403 but allowed this. A ticket you cannot open is a 404. Posting does not mark the thread read on your behalf.
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | ||
| task_uuid | Yes | The ticket's uuid, from list_tickets, get_board or search |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral disclosure burden. It discloses markdown parsing, concurrency safety (no version, cannot 409), role permissions (commenter allowed), the 404 for inaccessible tickets, and that posting does not mark the thread read. This is exceptionally transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place. It starts with the core action, then flows through markdown behavior, concurrency, permissions, error conditions, and side effects. No fluff exists; the length is justified by the high information content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema and the tool's role as a write operation, the description covers all necessary context: behavior, error cases (404), authorization limits, concurrency safety, and post-condition (no read status). An agent has everything needed to select and invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50%: task_uuid has a description but body does not. The description adds vital semantics for body by explaining Markdown parsing, and explicitly notes the absence of a version parameter, which clarifies the parameter space beyond the schema. This fully compensates for the low schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence 'Add a comment to a ticket' uses a specific verb plus resource, making the purpose immediately clear. It further distinguishes itself from update_ticket by explicitly framing it as the right way to report something rather than editing the description out from under someone. No ambiguity exists.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly contrasts with update_ticket and move_ticket, noting that a commenter role is refused those but allowed this. It identifies clear when-to-use scenarios: concurrent edits and read-only key contexts. This is strong guidance for selecting this tool over alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_automationA
Create an automation rule on a board from a trigger, one to twenty actions, and optional conditions. READ THIS BEFORE CALLING IT: the rule runs as the user this API key acts as, every time it is triggered, for as long as it exists — a standing grant of that person's permissions to anybody who can cause the trigger, not a one-off write like every other tool here, and the resulting history is attributed to them. This one takes effect immediately: the rule is live as soon as it is created and fires on its trigger within a couple of seconds, so do not create one speculatively to see what it would do. Creating one requires a workspace OWNER or ADMIN — a member who can otherwise write on the board is a 403 and retrying cannot fix it. A 402 means the plan's per-board rule limit is already reached (two on free) and nothing was created. Conditions are a flat AND of field/operator/value tests and every one must hold; the operators a field accepts differ, so title contains x is valid where title is x is a 400 listing the operators title takes. run_as_user_uuid defaults to the key's own user, and naming somebody else is refused unless they are an active member who can write and can see the board. Rules arrive switched on unless you pass enabled: false. The schedule and ticket.due triggers are not events and REQUIRE trigger_config: a schedule fans out over every ticket the conditions select at its appointed time, in UTC, so give it conditions unless you really mean the whole board; a due-date rule fires once per ticket per threshold and never retroactively, so creating one does nothing to work that is already overdue. column.empty is the odd one: it is about ONE ticket rather than the board, so it requires a task_uuid alongside trigger_config: {status_uuid}, and it fires the moment that column holds nothing — which may be the moment you create it, since a column that is already empty is already empty. It also keeps doing it every time that column empties again, for ever, unless you pass run_limit; one-off queue entries are what people usually mean, so pass run_limit: 1 unless a standing arrangement was actually asked for. create_linked_ticket is the exception to all of that: it CREATES a ticket rather than changing the triggering one, it needs a title (templated, so {{now}} makes a weekly checklist a new ticket each week) and takes an optional description, status_uuid and relationship (blocks, blocked_by, related_to, duplicate_of, stated from the new ticket's point of view). A schedule rule whose actions are ONLY this fires once for the board rather than once per ticket, which is what makes "every Monday, create the release checklist" work — and such a rule is refused if it also carries conditions, a relationship, or any action that acts on a ticket, because none of those has a ticket to be about. A ticket-triggered rule using it feeds itself, and is stopped by the depth cap after three chained runs rather than looping.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| actions | Yes | Each action is an object with a `type` from the list and whatever that action needs — a status, a user, a label, a comment body | |
| enabled | No | ||
| run_limit | No | column.empty only, and every other trigger refuses it. How many times the rule may move the ticket before it retires itself: a ticket can leave that column and the column can empty again, and without a limit the rule pulls it back in every time, indefinitely. Pass 1 unless a standing arrangement is what was actually asked for. A finished rule comes back from list_automations switched off with a `disabled_reason` saying so, and switching it back on does not give it more runs. Counts the runs that acted, successes and failures alike. Cannot be changed afterwards | |
| task_uuid | No | column.empty only, where it is required: the ticket the rule moves. Every other trigger refuses it, since those act on the ticket that set them off. Cannot be changed afterwards — a rule about the wrong ticket has to be deleted and made again | |
| board_uuid | Yes | The board's uuid, from list_boards | |
| conditions | No | A flat AND — every condition must hold. No OR and no nesting. Omit for every occurrence of the trigger | |
| trigger_type | Yes | ||
| trigger_config | No | Required for the schedule, ticket.due, ticket.in_column and column.empty triggers and ignored by every other one. A schedule needs {interval, at}; a due-date rule needs {when} plus {days} unless when is "arrives"; a dwell rule needs {status_uuid, days}; a column.empty rule needs {status_uuid}. | |
| run_as_user_uuid | No | Defaults to the user this key acts as. Read the warning above |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully covers behavioral implications: immediate activation, persistent standing permissions, attribution to the key's user, non-retroactive due-date rules, column.empty firing on already-empty columns, and the run_limit mechanism. It also discloses error responses (403, 402, 400) and the depth cap for self-feeding rules.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although long, the description is densely packed and every sentence delivers unique, actionable information. It is front-loaded with the core purpose and critical warning, then systematically addresses triggers, conditions, and edge cases without redundancy. The structure uses natural paragraphs to separate concerns.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 10 parameters, nested objects, no annotations, and no output schema, the description is remarkably complete. It covers permission requirements, failure modes, defaults, timing semantics, per-trigger behavior, and even the exception for create_linked_ticket-only schedule rules. It leaves little ambiguity for an AI agent deciding whether and how to invoke the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds substantial meaning beyond the schema: explains run_as_user_uuid default, enabled default, run_limit semantics, and the relationship between trigger_config and trigger types. It also elaborates on create_linked_ticket action parameters (title templating, relationship options) that the schema's generic `additionalProperties` does not specify.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: "Create an automation rule on a board from a trigger, one to twenty actions, and optional conditions." It clearly distinguishes this from sibling tools by contrasting it with "a one-off write like every other tool here" and by singling out the create_linked_ticket action as an exception within the tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use and when-not-to-use guidance: warns against speculative creation, explains permission requirements (OWNER/ADMIN), and details conditions under which the rule is refused. It also contrasts with other tools by stating this is a standing grant of permissions, not a one-off write, and names specific scenarios (e.g., schedule-only create_linked_ticket rules) where behavior differs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_boardA
Create a board in a workspace. Without template the board arrives with Laver's default columns; with one it is seeded with that template's columns, labels and a few example tickets. template accepts exactly "crm" or "sales-leads" — anything else is a 400 from the schema, so do not guess an id. A template applies once, at creation: there is no way to apply one to an existing board and no link back afterwards, so a board created without one has to be arranged by hand. A workspace you cannot open is a 404, and a 402 means the plan's board limit is already reached and the board was not created — neither is worth retrying.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| template | No | Omit for the default columns | |
| workspace_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses behavior: template seeding, one-time application, no link back afterwards, manual arrangement without template, and specific error outcomes (404 workspace, 402 limit). This goes far beyond basic 'creates a board'.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a dense but well-organized paragraph. It front-loads the core action, then details template behavior, then error conditions. Every sentence earns its place with useful, non-redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no annotations and no output schema, the description covers everything an agent needs to invoke safely: template constraints, irreversibility, error handling, and plan limits. It is sufficiently complete for a creation tool with three parameters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 33%, but the description compensates for the template parameter by specifying exact allowed values and the 400 error for anything else. It also adds meaning for workspace_uuid by implying it must be an openable workspace. 'name' receives no extra semantic detail, but schema min/max length suffice.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Create a board in a workspace', a specific verb and resource. It distinguishes from sibling tools like list_boards and get_board by covering creation semantics, and clarifies template-driven behavior.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly implies when to use (creating a board) and provides decision-relevant context: what happens with/without template, and which errors are not worth retrying. It does not explicitly name alternative tools, but the create vs. read distinction is clear from siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_ticketA
Create a ticket on a board. Give either status (the column name) or status_uuid; with neither it lands in the first column. Markdown in description is parsed — headings, lists and code fences all render. It is appended to the bottom of its column unless you send a position. The reply is the new ticket, including the uuid to reference it by and the version any later write to it will need, so there is no need to re-read it before your next call. This tool sends no idempotency key, so calling it twice makes two tickets: on a timeout or an unclear failure, use list_tickets with q set to the title to see whether the first one landed before you retry.
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | The ticket's title, 1 to 500 characters. This is the whole of what shows on the card, so put the specifics here rather than in the description alone. | |
| status | No | The column to place it in, by name as shown on the board. Must be a column on this board. Send this or `status_uuid`, not both; with neither it lands in the first column. | |
| due_date | No | Due date as an ISO 8601 date, e.g. 2026-08-14 | |
| position | No | Sort key within the column, not an index — smaller sorts higher. Read the neighbours' `position` off get_board and send a number between them; omit it to append to the bottom. Positions come back as decimal strings ('1000.000000') and are sent as numbers. | |
| priority | No | One of low, medium, high or urgent. Left off, the ticket simply has no priority set, which is not the same as low. | |
| board_uuid | Yes | The board to create it on, from list_boards | |
| description | No | The body, Markdown, up to 20000 characters. Headings, lists and code fences all render. | |
| status_uuid | No | The column to place it in, by uuid, from get_board. The exact form of `status` and the one to prefer once you have read the board, since renaming a column does not break it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries full responsibility for behavioral disclosure. It thoroughly explains Markdown parsing in the description field, the behavior of landing in the first column when neither status nor status_uuid is given, position-based ordering, and the idempotency caveat that calling twice creates two tickets. It also reveals the response structure (uuid and version), making the agent aware of post-call implications.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence provides critical operational detail: parameter selection, Markdown behavior, positioning, response contents, and retry strategy. It is front-loaded with the core action and flows logically without redundant phrases, making it efficient for agent consumption.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a create operation with 8 parameters and no output schema, the description fully compensates by explaining the return value (new ticket with uuid and version), the default column behavior, and the idempotency caveat. It also covers failure handling via list_tickets. This is a complete context for an agent to invoke the tool correctly and follow up appropriately.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (all 8 parameters have descriptions), so the baseline is 3. However, the description adds valuable semantic depth beyond the schema: it clarifies the mutual exclusivity of status and status_uuid, explains that position is a sort key not an index, and notes that returned positions are decimal strings sent as numbers. This goes above the baseline by providing practical contextual semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Create a ticket on a board', which is a specific verb+resource statement that clearly defines the tool's purpose. It also distinguishes the tool from siblings like update_ticket, archive_ticket, and delete_ticket by stating it creates new tickets, not modifying or deleting existing ones.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage guidance such as choosing between status/status_uuid and handling timeouts by using list_tickets with the title as a fallback. This gives the agent clear direction on when to use this tool and how to recover from ambiguous failures, which is a strong alternative-oriented explanation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_wiki_pageA
Write a NEW page into a wiki, with its body as markdown. This is how an agent puts findings somewhere durable instead of handing them back as chat text. wiki_uuid comes from list_wikis; parent_page_uuid (from get_wiki_tree) nests the new page under an existing one and is where a page belongs unless it is genuinely top-level. The markdown is converted server-side by the same parser ticket descriptions and comments go through — headings, lists, tables, code blocks, blockquotes, horizontal rules and links all survive. An  image survives too, as a reference to that URL — but only for a URL already hosted somewhere public, because there is no tool here to upload an attachment, and an image the reader cannot fetch renders as a broken one. Raw HTML is kept as literal text rather than interpreted, so do not reach for it to get something markdown lacks. ADD ONLY: there is deliberately no tool to change or delete what is already on a page. Wiki pages have a live collaborative editor behind them, so a whole-document overwrite from here would silently destroy whatever a person had open at the time. Adding cannot damage anything, so it is offered and overwriting is not. Do not call this twice to 'update' a page; you will get two pages — to add to a page that already exists, use append_wiki_page. Creating a page needs write access to the workspace: a read-only role or a guest key is a 403 and retrying cannot fix it. A title is required and a body is not, so a page can be created empty and filled in by a person later.
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | ||
| wiki_uuid | Yes | ||
| content_markdown | No | The page body as markdown. Omit for an empty page. Longer than 100k characters is refused rather than truncated | |
| parent_page_uuid | No | Nest under this page. Must be in the same wiki — a page from another wiki is a 400, not a silent move |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full disclosure burden and succeeds: it reveals create-only semantics, no overwrite/delete, duplicate-page behavior, server-side markdown conversion, image URL requirements, raw HTML literal handling, and permission failures. It even explains the underlying reason (collaborative editor) for not offering overwrite.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average but every sentence adds substantive value: usage context, parameter origins, markdown behavior, mutation safety, permission notes, and requirements. It is organized into logical paragraphs and is front-loaded with the core purpose. No filler or redundant phrases.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is exceptionally thorough for purpose, parameters, and safety, but with no output schema it does not mention what the tool returns (e.g., the new page's UUID). This is a minor gap for an agent that may need a handle for subsequent operations. Everything else needed for selection and correct invocation is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite only 50% schema description coverage, the description enriches all parameters: wiki_uuid is sourced from list_wikis, parent_page_uuid from get_wiki_tree and nests under an existing page, content_markdown receives markdown-feature details and the optionality of body, and title is confirmed required. This compensates fully for schema gaps.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Write a NEW page into a wiki, with its body as markdown' – a specific verb, resource, and scope. It explicitly distinguishes itself from append_wiki_page by stating 'Do not call this twice to update a page... use append_wiki_page instead.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear guidance on when to use: 'This is how an agent puts findings somewhere durable instead of handing them back as chat text.' It also gives explicit exclusions and alternatives: 'to add to a page that already exists, use append_wiki_page,' and warns against calling twice. Permission prerequisites (write access, 403 for read-only/guest) are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_ticketA
Permanently destroy a ticket that is ALREADY in the trash, along with its comments and attachments. Nothing undoes this — not the trash, not a restore, not support. It is the second step and never the first: archive_ticket, then this. A ticket still live on a board answers 404 rather than being destroyed, so this cannot bin something in one call. workspace_uuid must be the workspace the ticket belongs to — from get_ticket before it was archived, or get_board for the board it was on. Naming a workspace your key was not issued for is a 403 (This API key is scoped to a different workspace.) and never reaches the ticket; naming your own workspace for a ticket that does not live in it is a 404. Keep this for tickets that should never have existed: a duplicate, a probe, a test fixture you created. Anything a person might want back can simply be left archived, where it expires on its own. The 404 is otherwise deliberately undistinguished — not archived, already destroyed, and a board you cannot open all look identical.
| Name | Required | Description | Default |
|---|---|---|---|
| task_uuid | Yes | The ticket's uuid, from list_tickets, get_board or search | |
| workspace_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and exceeds it. It discloses irreversibility ('Nothing undoes this'), the full scope (comments, attachments), 404 behavior for live tickets, 403 for wrong workspace, and the intentionally undistinguished 404. This is rich behavioural context that will prevent misuse.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although the description is long, every sentence is purposeful and front-loaded with critical information. The first sentence states the destructive action and scope, then goes on to prerequisites and error semantics. No fluff; the redundancy ('Permanently destroy' / 'Nothing undoes this') reinforces the gravity of the operation without wasting words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity and irreversibility of the operation, the description covers prerequisites (must be archived), sources for parameters, error conditions (403/404 distinctions), and appropriate use cases. With no output schema, it also hints at what happens on success (the ticket is destroyed) and what errors look like. It is complete for an agent to invoke safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50% (only task_uuid has a description). The description compensates by explaining exactly how to choose workspace_uuid: 'must be the workspace the ticket belongs to — from get_ticket before it was archived, or get_board for the board it was on.' It also explains error cases that depend on workspace selection. This adds significant meaning beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Permanently destroy a ticket that is ALREADY in the trash, along with its comments and attachments.' This is a specific verb+resource+condition that distinguishes it from siblings like archive_ticket. It further clarifies that live tickets return 404, so the purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells when to use: 'It is the second step and never the first: archive_ticket, then this.' It also provides strong guidance on what to keep vs. destroy: 'Keep this for tickets that should never have existed... Anything a person might want back can simply be left archived.' This fully differentiates from archive_ticket and other alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_ticket_attachmentA
Remove a file from a ticket. This is the same one-way-but-recoverable move archive_ticket makes: the attachment leaves the ticket immediately and its bytes sit in the workspace trash for 30 days, after which the sweep destroys them. There is no tool here to restore one — that is the web app — so treat it as final in an agent's hands. The freed bytes stop counting against the workspace's storage quota straight away. An attachment already deleted, still uploading, or on a ticket this key cannot open are all the same 404, so this never confirms that a file existed.
| Name | Required | Description | Default |
|---|---|---|---|
| task_uuid | Yes | The ticket's uuid, from list_tickets, get_board or search | |
| attachment_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It fully delivers: the attachment moves to workspace trash for 30 days, storage quota is freed immediately, there is no restore tool, and all error cases (already deleted, still uploading, or inaccessible ticket) return the same 404. This is exceptionally transparent and goes far beyond a generic 'delete' statement.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first sentence states the core action immediately. Every subsequent sentence earns its place by adding vital operational details: the 30-day trash window, the lack of a restore tool, the immediate quota effect, and the unified 404 behavior. It is thorough without being bloated, and the progression from action to consequences is logical.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that there is no output schema and no annotations, the description is remarkably complete. It explains what happens to the attachment, how long it persists, what the agent cannot do afterward, the storage-quota side effect, and the exact error semantics. This is more than sufficient for an agent to safely reason about invoking the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 50%: task_uuid has a descriptive source hint, but attachment_uuid has no description. The tool description does not compensate by explaining what attachment_uuid is or how to obtain it. The phrase 'Remove a file from a ticket' only weakly implies the attachment identifier's role. Since half the parameters are undocumented and the description adds no specific parameter guidance, the semantics are under-served.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with a specific verb and resource: "Remove a file from a ticket." It clearly distinguishes this from sibling tools like delete_ticket or archive_ticket by specifying that it operates on attachments and shares the trash mechanism with archive_ticket. The purpose is unambiguous and instantly recognizable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear usage context: removing an attachment is one-way-but-recoverable, and there is no restore tool available to the agent, so the agent should treat it as final. It does not explicitly name alternative tools or state when not to use it, but the context strongly implies the appropriate scenarios. The finality warning serves as practical guidance for deciding when to invoke the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_boardA
A board with its status columns, labels, members and tickets. The status uuids returned here are what create_ticket and move_ticket expect. Each ticket carries label_uuids — bare uuids, resolvable against the board's labels list in the same reply — which is exactly the field update_ticket takes, so a label read from here can be written straight back without remapping. Note that list_tickets and get_ticket still return expanded labels objects, because neither reply has the dictionary to resolve uuids against. The tickets are around 90% of this reply, so pass include_tasks: false when you came for the uuids.
| Name | Required | Description | Default |
|---|---|---|---|
| board_uuid | Yes | The board's uuid, from list_boards | |
| include_tasks | No | Set false to leave the tickets out and get the board's structure alone — statuses, labels, members, custom fields and `server_time`. That is what you want when you called this for a status_uuid or a label_uuid, and it is roughly a tenth of the bytes. Defaults to true, and the reply with it left out is unchanged. Use list_tickets when you want the tickets, since that one takes `limit` and `status`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Without annotations, the description discloses key behaviors: the UUID format (bare vs expanded), the payload size (~90% tickets), and the semantics of the returned data for interop with create_ticket, move_ticket, and update_ticket. This goes beyond a simple read operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, each adding distinct value: resource definition, UUID semantics, cross-tool comparison, and performance guidance. No redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema or annotations, the description covers the return structure, UUID resolution, payload size, and usage alternatives, making it sufficient for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds context beyond the schema by explaining why include_tasks matters (response size) and what the returned UUIDs are used for. It complements the schema's descriptions of board_uuid and include_tasks, but the schema already covers parameter basics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens by defining the resource ('A board with its status columns, labels, members and tickets') and explains the significance of the returned status and label UUIDs, distinguishing it from sibling tools like list_tickets and get_ticket by showing how the data format differs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly names alternatives: list_tickets and get_ticket return expanded labels, and list_tickets is recommended for ticket listing since it supports limit and status. It also advises setting include_tasks=false when only UUIDs are needed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_ticketA
One ticket in full: its fields, its subtasks, its expanded labels objects, and the version every write needs. Read this before you write. update_ticket, move_ticket, archive_ticket and the rest all take that version and answer 409 if it has moved on since you read it — that 409 is the signal to call this again and retry against the fresh version, never to drop the version or force the write. labels arrive expanded rather than as bare uuids because this reply carries no board dictionary to resolve them against; get_board returns label_uuids instead, and that bare-uuid shape is the one update_ticket wants. A ticket you cannot open answers 404, the same 404 as one that never existed, so a 404 here is not proof the ticket is gone.
| Name | Required | Description | Default |
|---|---|---|---|
| task_uuid | Yes | The ticket's uuid, from list_tickets, get_board or search |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses the expanded label format and why (no board dictionary in this response), the 409/version interaction, and the ambiguous 404 ('the same 404 as one that never existed'). This is rich behavioral context beyond a simple 'get a ticket' statement, exactly what an agent needs to interpret responses correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence adds value: response contents, usage guidance, label format explanation, and error semantics. It is front-loaded with the core meaning and structured logically, making it efficient despite its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must cover return values and edge cases. It explains the essential parts of the response (version, expanded labels) and clarifies error semantics (409 retry, ambiguous 404). Given the tool's simplicity (one parameter) and the thorough description, the agent has everything needed to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% description coverage for the only parameter, `task_uuid`, including its source. The description adds no additional parameter-specific semantics, so the baseline score of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'One ticket in full: its fields, its `subtasks`, its expanded `labels` objects, and the `version` every write needs.' This is a specific verb+resource with a clear scope, and it implicitly distinguishes get_ticket from siblings like list_tickets or get_ticket_comments by enumerating what is included in the full ticket representation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs 'Read this before you write' and explains that update_ticket, move_ticket, and archive_ticket all require the version field and will respond with 409 if it is stale. It further details the retry behavior and clarifies the difference between get_ticket's expanded labels and get_board's bare `label_uuids`, leaving no doubt about when to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_ticket_attachmentA
Fetch one attachment's CONTENT, having found its uuid with list_ticket_attachments. What comes back depends on what the file is, because a tool result is text and most files are not: a text or CSV attachment is returned inline as text; an image is returned as an image block, which is the only form the model can actually look at; anything else — a PDF, a spreadsheet, a document — cannot cross this boundary as text at all, and needs save_to. Pass save_to for any file you want on disk, and for anything large: it writes the bytes to that path on the machine THIS SERVER runs on (normally the same machine as the agent, since the client starts it as a subprocess) and returns the path and size instead of the content. Files over 4 MB always need save_to, whatever their type, because an inline result that size costs more context than the answer is worth. Missing, still uploading, deleted, or on a ticket you cannot open are all the same 404.
| Name | Required | Description | Default |
|---|---|---|---|
| save_to | No | Absolute path to write the file to, on the machine running this server. Parent directories are created. An existing file is overwritten | |
| task_uuid | Yes | The ticket's uuid, from list_tickets, get_board or search | |
| attachment_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden and does so thoroughly: it discloses content-type-dependent returns, server-side file writing, path/size return instead of content, the 4 MB context-cost rule, and the unified 404 for missing/uploading/deleted/permission-denied attachments. This is excellent disclosure beyond anything structural annotations could provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but front-loaded with the core action and every clause provides necessary operational detail about file-type behavior and save_to. It is dense rather than wasteful; a bit of tightening would make it more concise, but the current length is justified by the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex tool with no annotations and no output schema, the description is complete: it covers invocation prerequisite, return behavior for all file categories, save_to usage and location, size thresholds, and error semantics. An agent has enough information to invoke the tool correctly and predict outcomes.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 67% with attachment_uuid lacking a description. The tool description compensates by stating the attachment_uuid comes from list_ticket_attachments and by adding deep semantics for save_to (server machine, path/size return, overwrite behavior). It adds clear meaning beyond the schema's bare field definitions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Fetch one attachment's CONTENT' and clarifies that the result depends on the file type, tying the attachment uuid to list_ticket_attachments. This clearly distinguishes the tool from sibling list/upload/delete attachment tools and states its exact resource and action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly instructs users to first find the uuid via list_ticket_attachments and gives conditional guidance: text/CSV inline, image blocks, and save_to for binary/large files with a 4 MB threshold. It lacks explicit 'when not to use' exclusions, but the context for using the tool and its save_to parameter is strong.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_ticket_commentsA
The discussion on a ticket, as two lists: comments — the thread, oldest first — and events, the activity history the web app draws alongside it (moves, field edits, who did what and when). The whole thread comes back in one call; there is no paging and no cursor, so a very long one is simply long. Reading is free of side effects: it does NOT mark the thread as read or clear anyone's unread badge, so polling this to follow a ticket cannot make a teammate think their comment has been seen. A ticket you cannot open is a 404, the same 404 a ticket that never existed gives.
| Name | Required | Description | Default |
|---|---|---|---|
| task_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Without annotations, the description carries the full burden. It discloses no paging/cursor behavior, no side effects (doesn't mark read), and consistent 404 for inaccessible/nonexistent tickets. This is rich, precise behavioral context beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise yet complete. Each sentence adds value: purpose, structure, pagination, side effects, error behavior. No filler or repetition. Front-loaded with the main output description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description explains the return format (two lists), ordering, pagination, side effects, and error semantics. It covers all critical aspects an agent needs to understand the tool's behavior. Very complete for a single-parameter read tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description does not compensate by adding meaning to the sole parameter task_uuid. It only vaguely references 'a ticket you cannot open', but never describes the parameter's role or format. With one obvious parameter, it's not catastrophic, but it fails the coverage requirement.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states what the tool does: returns the discussion on a ticket as two lists (comments and events). It distinguishes from siblings like get_ticket (ticket details) and list_ticket_attachments (attachments) by explicitly mentioning the content and structure.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for reading and following a ticket thread, mentioning 'polling this to follow a ticket' and noting lack of side effects. It does not explicitly name alternatives, but the context is clear enough for an agent to select it over write-oriented siblings like comment_on_ticket.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_wiki_pageA
One wiki page in full, with its content and any attachments it references. Get page_uuid from get_wiki_tree or from a search_wiki hit — a page uuid is not a wiki uuid, and passing one for the other is a 404. A page you cannot open is that same 404, so it never confirms that a page exists. This server can create a page (create_wiki_page) and add to the end of one (append_wiki_page), but cannot change or delete what is already written: to correct something, append the correction rather than planning to edit the page.
| Name | Required | Description | Default |
|---|---|---|---|
| page_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Although no annotations are provided, the description discloses key behaviors: the return includes content and attachments, a 404 does not distinguish between nonexistent pages and access restrictions, and the server cannot edit/delete pages. This is thorough transparency for a read operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is 110 words across four sentences, but every sentence adds value: purpose, identifier acquisition, 404 semantics, and server limitations. It is front-loaded with the primary purpose and efficiently packed with actionable context.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one parameter, no annotations, and no output schema, the description covers purpose, parameter source and caveats, error behavior, and relevant server capabilities. It is remarkably complete, leaving no critical operational question unanswered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema coverage, the description compensates by explaining that page_uuid is a page identifier obtained from get_wiki_tree or search_wiki, and crucially that a page uuid is not a wiki uuid. This adds significant meaning beyond the raw UUID format in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states 'One wiki page in full, with its content and any attachments it references,' clearly identifying the tool as fetching a single wiki page with its full content and attachments. It distinguishes from siblings like get_wiki_tree and get_wiki_page_version by indicating it provides the full current page rather than structure or versions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells the agent where to obtain the required page_uuid: 'Get page_uuid from get_wiki_tree or from a search_wiki hit.' It warns against confusing page uuids with wiki uuids, clarifies the 404 behavior, and advises using append to correct content when editing is impossible. This provides clear when-to-use and caveats.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_wiki_page_versionA
What a wiki page said at an earlier version — its title, content and who saved it. This is how you recover something that was overwritten, and it is a READ: the page is not changed and its version does not move. Reach for it the moment you find that a page no longer says what you put there. version counts from 1 and goes up by one on every save; the current version is on the page from get_wiki_page. A version that was never saved, and a page you cannot open, are both the same 404. There is deliberately no tool here that puts an old version back — read it and append the wording you want, because a restore overwrites whatever a colleague has open in the live editor right now.
| Name | Required | Description | Default |
|---|---|---|---|
| version | Yes | Which save to read. 1 is the page as first created; get_wiki_page reports the current number | |
| page_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully discloses behavior: it states the operation is a READ, the page is not changed and its version does not move. It also explains error semantics (404 for invalid version or inaccessible page) and explicitly notes there is no restore capability.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose, then flows naturally into usage, parameter semantics, error behavior, and a warning. Every sentence contributes value without redundancy, making it appropriately sized for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although there is no output schema, the description covers the return content (title, content, who saved it). It also addresses edge cases (never-saved version, inaccessible page) and provides critical guidance about not restoring old versions, making the description fully complete for this read-only tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema describes only one of two parameters (version). The description adds significant semantics for version: counting from 1, incrementing per save, and referring to get_wiki_page for the current number. However, page_uuid is not elaborated beyond the schema's format/pattern, leaving a small gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: retrieving what a wiki page said at an earlier version, including title, content, and author. It explicitly distinguishes itself from sibling tools by referencing get_wiki_page for the current version and noting there is no restore tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: 'This is how you recover something that was overwritten' and 'Reach for it the moment you find that a page no longer says what you put there.' It contrasts with get_wiki_page and warns against attempting a restore, steering users toward read + append.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_wiki_treeA
Every page in a wiki as a tree — titles, uuids and nesting — without any page content. This is the cheap way to find a page uuid when you know roughly where it sits; search_wiki is the way when you know roughly what it says. Follow up with get_wiki_page for the content of one. A wiki you cannot open is a 404, the same one a wiki that does not exist gives. A big wiki is tens of thousands of tokens of metadata, so narrow it with parent_page_uuid and depth rather than reading all of it to find one uuid.
| Name | Required | Description | Default |
|---|---|---|---|
| depth | No | How many levels of nesting to return. 1 is the top level alone — the whole wiki's top-level pages, or just `parent_page_uuid` itself when one is given; 2 adds their children. Omit for every level. | |
| wiki_uuid | Yes | ||
| parent_page_uuid | No | Return only this page and everything nested under it. The uuid must be a page in THIS wiki — anything else is an error naming it, not an empty tree. Omit for the whole wiki. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the transparency burden. It discloses the return contents (titles, uuids, nesting, no page content), the 404 behavior for unopenable/nonexistent wikis, and the token cost of large wikis. It does not explicitly state read-only or describe the output tree structure in detail, but it covers key behavioral traits sufficiently.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is four sentences, each earn its place: it states the core output, positions against alternatives, explains the 404 case, and gives performance advice. Front-loaded and free of redundant filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and lack of output schema, the description is remarkably complete. It covers what the tree contains, when to use it over alternatives, how to follow up with get_wiki_page, error semantics for inaccessible wikis, and scaling guidance for large wikis. An agent can confidently select and invoke this tool based on this description alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67%, with depth and parent_page_uuid already having descriptions. The description adds meaning by recommending these parameters for narrowing large trees ('narrow it with parent_page_uuid and depth rather than reading all of it'). wiki_uuid lacks a description but is inherently clear from its name and format.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states exactly what the tool does: 'Every page in a wiki as a tree — titles, uuids and nesting — without any page content.' It identifies the specific verb (get), resource (wiki tree), and output scope clearly. It also distinguishes itself from siblings by directly referencing search_wiki and get_wiki_page, making its unique role explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit guidance: 'This is the cheap way to find a page uuid when you know roughly where it sits; search_wiki is the way when you know roughly what it says.' It also advises when to use depth and parent_page_uuid to avoid reading large trees, which is practical usage direction.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
link_ticketsA
Record that one ticket must be finished before another can start. Direction is from the point of view of task_uuid: "blocks" means task_uuid has to be done first. Every ticket read afterwards carries blocked_by, blocks and is_blocked, so this is how you work out what order to do things in.
| Name | Required | Description | Default |
|---|---|---|---|
| direction | Yes | Read from task_uuid's side: 'blocks' means task_uuid must be finished before other_task_uuid can start; 'blocked_by' means the reverse. Unlink with unlink_tickets. | |
| task_uuid | Yes | The ticket the link is stated from, and the one `direction` is read relative to. Swapping this with other_task_uuid reverses the meaning, so decide which end you are describing before you call. | |
| other_task_uuid | Yes | The ticket at the far end of the link. Must be a different ticket from task_uuid. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it explains direction is from task_uuid's perspective, defines 'blocks' vs 'blocked_by', and discloses that ticket reads afterwards include blocked_by, blocks, and is_blocked. It doesn't cover edge cases like idempotency or errors, but the key behavioral traits are communicated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with purpose, then direction rule, then read-effect. Every sentence earns its place with no redundancy. Excellent structure.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no annotations and no output schema, the description provides the essential context: purpose, directionality, and the impact on subsequent ticket reads. It could briefly mention removal via unlink_tickets, but that's available in the schema, so the overall coverage is strong.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and every parameter already has a detailed explanation. The description reinforces the direction semantics but doesn't add new parameter meaning beyond what the schema provides, matching the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Record that one ticket must be finished before another can start.' This clearly defines the tool's purpose and inherently distinguishes it from other ticket operations like create/update/delete, while also conveying the core directionality concept.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains when to use the tool ('this is how you work out what order to do things in') and notes the downstream effect on ticket reads, which gives practical context. However, it does not explicitly mention alternatives or when not to use it; for example, unlink_tickets only appears in the schema, not the description.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_automationsA
The automation rules on a board — each with its trigger, conditions, actions, whether it is enabled, the user it runs as, and the version any edit would need. These are live: a rule with enabled: true fires on its trigger within a couple of seconds and applies its actions as the person named in run_as_user_uuid. Read this before creating a rule on a board you did not set up, both because the per-board limit counts what is already here (two on the free plan) and because an existing rule may already do what you were about to add. There is no tool for run history, though the API has one (GET /boards/:board_uuid/automations/:rule_uuid/runs), so a rule's outcomes have to be read there rather than here. A rule that switched itself off after looping shows up here as enabled: false. Board access is enough to read them, which is wider than creating one. A board you cannot open is a 404, the same 404 a board that never existed gives.
| Name | Required | Description | Default |
|---|---|---|---|
| board_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure and does so thoroughly. It explains the live nature of enabled rules (fire within seconds, act as run_as_user_uuid), the per-board limit, the self-disabling after loops, the read access being wider than creation, and the 404 behavior for inaccessible boards. This is rich, non-obvious behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence contributes: it covers return fields, live behavior, usage timing, an API alternative, an edge case (self-disabling loop), and error semantics. It is front-loaded with the core purpose and then adds structured, relevant details without fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the single parameter, no output schema, and no annotations, the description is remarkably complete. It explains what is returned (fields), how the data behaves, what access is needed, the limitation of no run-history tool, and error semantics. An agent can confidently select and invoke this tool without further documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has only board_uuid with 0% description coverage, so the description must compensate. It does not restate the parameter but provides meaningful context: board_uuid refers to the board being read, board access is sufficient, and inaccessible/nonexistent boards yield the same 404. This adds behavioral meaning beyond the schema's format/pattern constraints.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description immediately identifies the resource as 'automation rules on a board' and enumerates the exact fields included (trigger, conditions, actions, enabled, run_as_user_uuid, version), making it clear this is a read-only listing distinct from creating automations. However, the opening is a noun phrase rather than an explicit verb phrase like 'Lists all automation rules for a board,' so it relies somewhat on the tool name to convey the action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage timing: 'Read this before creating a rule on a board you did not set up.' It also points out a sibling alternative (creating automations) and notes the lack of a run-history tool, directing users to the API. The wide read access vs. creation permission is explicitly contrasted, providing clear when-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_boardsA
The boards in a workspace, with their uuids and names — where you go from a workspace to something you can actually read. It lists only the boards this key can open, so a short list means limited access rather than an empty workspace, and archived boards are not in it. The uuid you want for get_board and list_tickets is here; the status uuids those need are not — get_board has those.
| Name | Required | Description | Default |
|---|---|---|---|
| workspace_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It reveals that results are access-filtered by the API key, that archived boards are excluded, and that only board uuids/names are returned (status uuids are elsewhere). This is meaningful context beyond a generic list operation, though it does not mention ordering, pagination, or read-only semantics explicitly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each with a distinct purpose: what the tool does, access/archive behavior, and what uuids are or aren't available. No filler, front-loaded with the core function.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple list tool with one parameter and no output schema, the description covers the result content (board uuids and names), access filtering, archive exclusion, and the relationship to downstream tools. It doesn't describe error responses or pagination, but those are less critical here; the given context is sufficient for an agent to decide when to call it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is only one parameter, workspace_uuid, and the schema already provides format and pattern validation. The description implicitly references the workspace in its first sentence, but does not explicitly explain that workspace_uuid is the identifier of the workspace whose boards are listed. With 0% schema description coverage, the description adds minimal extra meaning beyond the parameter name.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly names the resource (boards) and the action (list), and specifies the output (uuids and names). It distinguishes itself from siblings by clarifying that this is the step from workspace to readable content, and by noting that status uuids are not provided here but via get_board.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear workflow guidance: it positions list_boards as the bridge from workspace to board-level tools, states which downstream tools (get_board, list_tickets) will need the board uuid, and directs the user to get_board for status uuids. It also explains the consequence of a short list (limited access) and that archived boards are omitted, which frames when to expect incomplete results.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_ticket_attachmentsA
The files on a ticket: name, content type, size in bytes and uuid, newest first. Nothing else here reads a ticket's files, so this is the way to find out whether the specification an agent is working from is actually a PDF somebody attached. get_ticket reports attachment_total, which is how you know whether calling this is worth a round trip. Only finished uploads are listed — an upload still in flight and a deleted file both read as absent — and the reply carries metadata, never the bytes; get_ticket_attachment fetches those one at a time.
| Name | Required | Description | Default |
|---|---|---|---|
| task_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite no annotations, the description discloses important behavioral traits: returns metadata never bytes, only finished uploads are listed (in-flight/deleted appear absent), and lists newest first. It also notes that the reply carries metadata, not the file content, which is a key limitation beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is information-dense yet concise. Each sentence earns its place: it states the output fields and ordering, contrasts with get_ticket_attachment, mentions the attachment_total signal, and clarifies handling of in-flight/deleted files. It's well-structured, starting with the core function.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully describes the return format (metadata fields), ordering, and limitations. It also provides surrounding context (get_ticket for count, get_ticket_attachment for content) that makes the tool's role in the API complete. Nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has only task_uuid with no description (0% coverage). The description compensates by framing the tool as listing 'files on a ticket', making it clear that task_uuid is the ticket/task identifier. While it doesn't explicitly define the parameter, the context and tool name make its meaning unambiguous.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states what the tool does: lists a ticket's files with metadata (name, content type, size, uuid) ordered newest first. It distinguishes from get_ticket_attachment (which fetches the bytes) and explicitly says 'Nothing else here reads a ticket's files', making it unique among siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit usage context: use this to inspect attachment metadata, e.g., checking if a spec is a PDF. It references get_ticket for the attachment_total count to decide if calling is worthwhile, and get_ticket_attachment for fetching bytes, effectively giving alternatives and guidance on when to use each.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_ticketsA
Tickets on a board, in board order, optionally filtered by status or by free text. This is what to read a column or walk a whole board with; get_board returns the same tickets but takes neither limit nor a cursor. Paging: limit defaults to 50 and caps at 200, and next_cursor comes back only on a FULL page — a short page is the end of the walk, so stop when it is absent instead of calling again, and pass it back verbatim when it is there. Following a board over time: send the server_time from a previous reply as updated_since and you get only what changed since, plus removed_task_uuids for tickets that left the board or went out of view. Use the server's clock for that rather than your own, which is the whole point of server_time — your clock can drift and silently skip a ticket. Archived tickets are never returned.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Free-text search over titles and descriptions, ranked by relevance. Whole words and prefixes, not mid-word substrings. A ranked reply cannot be paged, so this cannot be combined with cursor. A UUID, or its first eight characters or more, is not searched for — it is RESOLVED: you get the ticket with that uuid and nothing else, and an empty list if there is none. It never falls back to text search, so a uuid that matches nothing means no such ticket rather than 'here are some tickets that mention it'. That sentence used to read 'a uuid finds that ticket' and was wrong in the way that costs you an afternoon: it was ranked text matching, so asking for a uuid returned every ticket whose prose quoted it — commonplace on a board where tickets cross-reference each other — with the one you asked for ranked LAST. Archived tickets are still not returned, by uuid or by text. | |
| limit | No | Tickets per page, 1 to 200. Defaults to 50. A reply holding exactly this many is a full page and carries a `next_cursor`; anything shorter is the last page. | |
| cursor | No | The `next_cursor` from the previous page, passed back unchanged. It encodes a position on the board, so it is only meaningful for the same board and the same filters — do not build one yourself or reuse one across a different query, and a cursor Laver cannot read is a 400. Cannot be combined with `q`: ranked results have no stable order to page through, and asking for both is a 400 telling you so. | |
| status | No | A status column's name, as shown on the board — 'To Do', 'In progress'. Matched against that board's columns, so a name from another board is a 400 rather than an empty list. Use status_uuid instead when you already hold the uuid, and do not send both. | |
| board_uuid | Yes | The board to read, from list_boards | |
| status_uuid | No | A status column's uuid, from get_board. The exact form of `status`, and the one to prefer once you have read the board, since it survives a column being renamed. | |
| updated_since | No | Return only tickets changed after this moment: an ISO 8601 timestamp, or — better — the `server_time` from a previous reply, which is the server's own clock and cannot drift against it. The reply then also carries `removed_task_uuids`, the tickets that left the board or went out of view since, which is the only way to notice a deletion by polling. | |
| include_descriptions | No | Set false to leave `description` off every ticket, keeping titles, statuses, labels and uuids. Bodies are around 78% of a column listing, and you have not chosen a ticket to read yet — get_ticket has the full one. Defaults to true, and the reply with it left out is unchanged. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it delivers. It discloses that `next_cursor` appears only on a full page and that a short page means the walk is over. It explains the UUID-resolution pitfall in `q` (that a UUID returns only that ticket, never fallback text search), and it explicitly states archived tickets are never returned. It also reveals `removed_task_uuids` as the only way to notice deletions by polling.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well front-loaded with purpose and then systematically covers paging, incremental refresh, and filters. However, it is quite lengthy, and the historical aside inside the `q` explanation ('That sentence used to read...') adds color but is not strictly necessary. Given the complexity and 8 parameters, the length is mostly justified, but a few sentences could be trimmed without losing essential guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is remarkably complete for a list tool with no output schema. It explains the return shape where relevant (paging cursor, removed_task_uuids, include_descriptions effect), covers all major behaviors (board order, filtering, paging, incremental updates, archived exclusion), and addresses edge cases like UUID resolution and cursor incompatibility with `q`. Nothing important is left unexplained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers all parameters at 100%, but the description adds substantial meaning beyond names and types. For `q` it explains the ranked behavior, the no-mid-word-substring restriction, and the special UUID resolution semantics. For `cursor` it warns that it is board- and filter-specific and that a mismatched cursor is a 400. For `updated_since` it recommends passing the previous `server_time` to avoid clock drift. The `include_descriptions` parameter is given quantitative context (78% of a column listing).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific statement: 'Tickets on a board, in board order, optionally filtered by status or by free text.' It names the exact resource (tickets on a board), the operation (listing), and the scoping options. It also distinguishes itself from a sibling tool by noting 'get_board returns the same tickets but takes neither `limit` nor a cursor.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use this tool ('This is what to read a column or walk a whole board with') and contrasts with get_board. It also gives guidance on paging (stop when next_cursor is absent), on using updated_since for following a board, and on include_descriptions=false to save payload while deferring to get_ticket for full details. This is textbook usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_wikisA
The wikis in a workspace, with their uuids — the only place a wiki_uuid comes from, so every other wiki tool starts here. workspace_uuid is required; start from list_workspaces if you do not have one. An empty list is not proof the workspace has no wiki: a key acting as a guest is excluded from workspace-wide reads and sees nothing here. Archiving a wiki (done in the browser; there is no tool here for it) takes it out of this list entirely, along with every page under it — pass archived: true to see those instead, which is also the only way to find a wiki_uuid for restore_wiki.
| Name | Required | Description | Default |
|---|---|---|---|
| archived | No | Omit or false for the normal list. true swaps it for archived wikis instead — the two never mix in one reply, the same way an archived wiki never appears beside a live one in the workspace sidebar. | |
| workspace_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses important behaviors: guest keys see nothing (empty list is not proof of no wiki), archiving removes wikis from the list, and the `archived: true` flag is the only way to get archived wiki UUIDs. This goes well beyond a basic description and proactively warns about hidden edge cases.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the primary purpose and each additional sentence provides necessary context (guest behavior, archiving, restore). It is slightly long but every sentence earns its place, with no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple listing tool with two parameters and no output schema, the description covers all essential aspects: entry-point workflow, guest edge case, archived-wiki behavior, and relationship to `restore_wiki`. It is a self-contained guide for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50% (only `archived` has a description). The description adds meaning for `workspace_uuid` (required, start from `list_workspaces`) and enriches the `archived` behavior (mutually exclusive lists, only way to find archived UUIDs). This compensates for the missing schema description effectively.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states it lists wikis in a workspace with their UUIDs, and distinguishes itself as the only source of `wiki_uuid`, making it the entry point for all other wiki tools. This is a specific verb+resource with explicit sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context: `workspace_uuid` is required and can be obtained from `list_workspaces`. It also explains the `archived` parameter and how it relates to `restore_wiki`. However, it does not explicitly mention when to use this tool over specific alternatives, though the entry-point role is strongly implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_workspacesA
The workspaces this key can act in, with their uuids, names and the role it holds in each. Start here when you do not already have a workspace uuid, then go to list_boards. Through an API key this is always exactly one workspace — the one the key was issued for — even when the person who created it belongs to several, so a single entry is the normal answer rather than a sign of missing access. Archived workspaces are not in it. Each entry carries billing_status, which is how you tell a workspace that has gone read-only from one you can still write to, before a write fails rather than after.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full responsibility for behavioral disclosure. It clearly states that via an API key the result is always exactly one workspace, that archived workspaces are excluded, and that each entry includes billing_status to differentiate read-only from writable workspaces. These are meaningful behavioral nuances that go beyond a simple 'list' definition.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet information-dense. Every sentence adds value: the result contents, the workflow context, the single-entry expectation, archived exclusion, and billing_status significance. No wasted words or repetition of schema information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with no output schema, the description provides a remarkably complete picture: what data is returned, how to interpret unusual results, what is not included, and how to use a key field for operational decisions. It fully equips an agent to invoke and interpret results correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the baseline is 4. The description appropriately focuses on output semantics rather than parameters, and no parameter explanation is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists the workspaces the API key can act in, including uuids, names, and the role held in each. It distinguishes itself from siblings by explicitly directing users to start here when they lack a workspace uuid and then proceed to list_boards, making the purpose and place in the workflow unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: 'Start here when you do not already have a workspace uuid, then go to list_boards.' It also explains the normal single-entry result to prevent misinterpretation, and highlights billing_status to detect read-only workspaces before attempting writes, which is valuable contextual guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
move_ticketA
Move a ticket to another column. Give either status (the column name) or status_uuid — with NEITHER this is a 400 ("body must NOT have fewer than 2 properties") and not a no-op, and with both it is a 400 saying to pick one. A column name is matched case-insensitively and trimmed, so "to do" finds "To Do", but a name no column has is a 400 naming it, and a status_uuid belonging to another board is refused too — get_board is where both come from. version must be the one get_ticket returned; a stale one is a 409 that carries the current version with it, so the retry does not need another read. The ticket is appended to the bottom of the column it arrives in unless you send a position; it does not keep the number it had in the column it left.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Target column name | |
| version | Yes | The `version` from the ticket as you last read it. Not a number you choose or increment: send back exactly what get_ticket gave you. If somebody else has written since, the call answers 409 rather than overwriting them — re-read with get_ticket and retry against the new version. | |
| position | No | Sort key within the column, not an index — smaller sorts higher. Read the neighbours' `position` off get_board and send a number between them; omit it to append to the bottom. Positions come back as decimal strings ('1000.000000') and are sent as numbers. | |
| task_uuid | Yes | The ticket's uuid, from list_tickets, get_board or search | |
| status_uuid | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure, and it excels: it details 400 errors for neither/both parameters, invalid column names, and wrong board UUIDs; explains 409 stale version handling with the current version included; and describes the append-to-bottom position behavior. This is rich operational detail that goes far beyond basic expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well-structured, starting with the core purpose and then systematically covering parameters, error cases, and positioning behavior. While every sentence provides value, the paragraph is long and could be broken into bullet points for easier scanning; slight verbosity in error message quotes earns a 4 rather than 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (concurrency versioning, positional semantics, multiple error modes) and the lack of annotations or output schema, the description is nearly complete. It covers inputs, side effects, and error handling thoroughly, but does not specify the nature of the success response (e.g., updated ticket or confirmation), which would be helpful in the absence of an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers 80% of parameters with descriptions, but the description adds crucial semantics: the mutual exclusivity of status and status_uuid, case-insensitive and trimmed matching, and the version requirement from get_ticket. These details are not in the schema and are essential for correct invocation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Move a ticket to another column,' which is a specific verb+resource statement that clearly distinguishes this tool from update_ticket (which likely modifies other fields). It further clarifies the domain by referencing get_board for column identifiers and get_ticket for versioning, solidifying its unique role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context: it explains the either/or requirement for status vs status_uuid, where to obtain these values (get_board), and how to handle versioning with get_ticket. It does not explicitly name alternative tools for exclusions, but the context is sufficiently clear for an agent to know when to invoke it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
restore_wikiA
Undo an archive: brings a wiki, and every page under it, back into list_wikis and back onto the internet if any of its pages were published. wiki_uuid comes from list_wikis with archived: true — that is the only place one is visible at all, since an archived wiki is invisible everywhere else this server reads. A wiki that is not archived, one in a workspace this key cannot open, and a uuid that does not exist are all the same 404, so this never confirms that a wiki exists. There is deliberately no tool here that archives one in the first place: that half stays a browser action.
| Name | Required | Description | Default |
|---|---|---|---|
| wiki_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so thoroughly. It discloses that the operation affects the wiki and all pages, may republish pages to the internet, that archived wikis are invisible elsewhere, that non-archived/nonexistent/wrong-workspace Uuids are indistinguishable, and that there is intentionally no archive tool. These behaviors are not in the schema or annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence in the description earns its place: purpose, parameter source, 404 ambiguity, and a note about missing archive functionality. The text is front-loaded with the core action, and the additional details are concise and relevant, not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with no output schema, the description covers the purpose, the source of the parameter, expected effects, error semantics, and a limitation. It is fully adequate for an agent to select and invoke the tool correctly without additional context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema only provides the type/format of wiki_uuid, but the description adds critical semantic meaning: it explains that the uuid must be from list_wikis with archived: true and that this is the only context where such a uuid is visible. This compensates fully for the 0% schema description coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Undo an archive: brings a wiki, and every page under it, back into list_wikis and back onto the internet if any of its pages were published.' This clearly states the action, the target, and the effect, and it differentiates the tool from siblings like archive_ticket or list_wikis.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly tells the user where to obtain the required parameter: 'wiki_uuid comes from list_wikis with archived: true — that is the only place one is visible at all.' It also provides an exclusion by noting that a wiki not archived, permission-denied, or nonexistent all return 404. This is clear when-to-use guidance with an alternative (list_wikis) and a caution.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchA
Search tickets, wiki pages AND ticket comments across a whole workspace in one call, ranked by relevance with a highlighted excerpt. Use this when you know roughly what something is called but not which board or wiki it is on — list_tickets needs a board_uuid, this does not. The reply has FOUR lists: boards and tasks and pages matched their own text, and comments are tickets found by something said ABOUT them — each carries the ticket it belongs to, and a ticket already in tasks is never repeated there. boards is easy to miss and is often the one you want: it is how you find the board a term names without listing every board first. Matches whole words and prefixes, not mid-word substrings: 'deploy' finds 'deployment', 'eploy' finds nothing. Three characters minimum. Returns a short line per hit, not the full ticket or comment — follow up with get_ticket, get_ticket_comments or get_wiki_page. Each excerpt marks the matched words with the control characters \x01 and \x02 rather than with markup; strip them before showing the text to anyone.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | ||
| limit | No | Results per kind, tickets and pages counted separately | |
| workspace_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses rich behavioral details: the four result lists, that boards is easy to miss, matching rules (whole words and prefixes, 3-char minimum), return format (short lines, not full content), and the control characters \x01/\x02 in excerpts. This goes well beyond a basic search description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence serves a purpose: core action, usage context, result structure, matching caveat, return granularity, and excerpt format. It is front-loaded with the main purpose and follows with essential details. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the lack of an output schema, the description thoroughly explains the four return lists, their semantics (comments are ticket references, no duplicates), and the pitfall with boards. It also covers matching constraints and follow-up actions. This is complete for a complex search tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 33% (only 'limit' is described). The description adds meaningful context about the query behavior (matching, minimum length) which relates to 'q', but it does not explicitly map each parameter by name. It also doesn't describe workspace_uuid beyond the search scope. Some compensation, but not complete.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and scope: 'Search tickets, wiki pages AND ticket comments across a whole workspace in one call, ranked by relevance with a highlighted excerpt.' It clearly distinguishes from the sibling list_tickets by noting that list_tickets requires a board_uuid whereas this tool does not.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly advises using this tool when the board or wiki location is unknown, and contrasts with list_tickets which requires a board_uuid. It also recommends follow-up tools (get_ticket, get_ticket_comments, get_wiki_page) for full content, but does not explicitly state when-not-to-use scenarios beyond the implicit contrast.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_wikiA
Full-text search inside ONE wiki, returning matching pages with a highlighted excerpt. A title match outranks a body match, so the page actually about the term comes first. A single word also matches as a prefix — 'custom' finds 'Customark' — while a multi-word query keeps the usual search semantics: "quoted phrase", -excluded, and OR. Matches are marked in the excerpt with the control characters \x01 and \x02 around each hit, not with markup; strip them before showing the text to anyone. wiki_uuid comes from list_wikis, and a wiki that does not exist or that you cannot open is the same 404. To search tickets and wikis together across a whole workspace, use search instead.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | ||
| wiki_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description takes full responsibility for behavioral disclosure. It reveals non-obvious behaviors: title matches outrank body matches, single-word prefix matching, multi-word query syntax, control-character delimiters in excerpts, and a 404 for missing/inaccessible wikis. This goes far beyond typical descriptions and equips the agent with critical operational knowledge.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence in this description earns its place. It opens with the primary purpose, then details matching behavior, excerpt formatting, source of the wiki UUID, error behavior, and an alternative tool reference. There is no fluff or repetition; it's information-dense yet still scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, lack of annotations, and lack of output schema, the description is remarkably complete. It covers purpose, query syntax, response formatting, error semantics, parameter provenance, and the sibling alternative. The only nominal gap is a detailed return structure, but the description adequately covers the highlighted excerpt behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does. It explains `wiki_uuid` as an identifier from `list_wikis` and gives a rich explanation of `q` semantics: prefix matching for single words, quoted phrases, excluded terms, and OR. This adds substantial meaning beyond the bare schema constraints.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Full-text search inside ONE wiki, returning matching pages with a highlighted excerpt.' It clearly distinguishes itself from the sibling `search` tool by scoping to a single wiki and later naming the alternative. This is a model of purpose clarity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage context: when to search within a single wiki, and directly states an alternative: 'To search tickets and wikis together across a whole workspace, use `search` instead.' It also mentions that `wiki_uuid` comes from `list_wikis`, providing a prerequisite chain. This clearly guides tool selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unlink_ticketsA
Remove the link between two tickets. Order does not matter — it finds the pair from either end, so you do not have to know which one is recorded as the blocker. Note what it removes: Laver can hold more than one kind of link between the same pair (a dependency, a related-to, a duplicate-of), a person can add the other kinds in the web app, and this removes ALL of them rather than only the dependency link_tickets creates. It is not an error to unlink a pair that was never linked — nothing matches, nothing is removed, and the call still succeeds — so this cannot be used to test whether a link exists; read blocked_by and blocks on the ticket for that. Either uuid being unreadable, on a board you cannot open, or nonexistent is a 404.
| Name | Required | Description | Default |
|---|---|---|---|
| task_uuid | Yes | The ticket's uuid, from list_tickets, get_board or search | |
| other_task_uuid | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully carries the behavioral disclosure burden. It reveals that the tool removes all link kinds (dependency, related-to, duplicate-of), not just the dependency created by link_tickets; that it is idempotent (no error if never linked); and that unreadable or nonexistent uuids yield a 404. These are critical behavioral traits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is detailed but each sentence earns its place. It starts with the main action, then explains link-type behavior, idempotency, and error conditions, using concrete terms like 'dependency' and 'related-to'. No filler or repetition exists.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's subtle behaviors (multiple link types, idempotency, 404 conditions) and the absence of annotations or an output schema, the description covers all aspects needed for correct invocation. It also points to `blocked_by` and `blocks` for existence checks, completing the contextual picture.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers only task_uuid with a description (50% coverage). The description adds crucial meaning: 'Order does not matter — it finds the pair from either end,' clarifying that the two parameters are symmetric. It doesn't explicitly describe other_task_uuid's provenance, but the order-independence note compensates for the partial schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Remove the link between two tickets', a clear verb+resource statement. It further explains that order does not matter and that it removes ALL link types, distinguishing it from sibling link_tickets. This provides a specific and unambiguous purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool: to remove a link regardless of which ticket is recorded as the blocker. It also gives a clear exclusion: 'this cannot be used to test whether a link exists; read `blocked_by` and `blocks` on the ticket for that,' pointing to alternative approaches.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_ticketA
Change a ticket. version must be the one from get_ticket; a 409 means somebody wrote first and you should re-read. Markdown in description is parsed, and it replaces the whole description rather than appending to it. label_uuids, assignee_uuids and custom_fields each REPLACE the whole set rather than adding to it, so send what is already on the ticket alongside what you are adding or you will silently remove the rest. custom_fields is keyed by the field's uuid — read them off get_ticket, since a name will not do — and a uuid no field on that board has is DROPPED SILENTLY rather than refused: the call answers 200 and the value is simply not there. The task in the reply carries the stored custom_fields, so read them back to confirm a write landed rather than assuming a 200 means it did.
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | Replaces the title. Omit to leave it as it is. | |
| status | No | Move it to this column, by name as shown on the board. Send this or `status_uuid`, not both. move_ticket is the tool for a move alone. | |
| version | Yes | The `version` from the ticket as you last read it. Not a number you choose or increment: send back exactly what get_ticket gave you. If somebody else has written since, the call answers 409 rather than overwriting them — re-read with get_ticket and retry against the new version. | |
| due_date | No | Due date as an ISO 8601 date, e.g. 2026-08-14. Explicit null clears it; omitting it leaves it unchanged. Those are different, so do not send null to mean 'no change'. | |
| position | No | Sort key within the column, not an index — smaller sorts higher. Read the neighbours' `position` off get_board and send a number between them; omit it to append to the bottom. Positions come back as decimal strings ('1000.000000') and are sent as numbers. | |
| priority | No | One of low, medium, high or urgent. Omit to leave it alone; there is no value here that clears a priority already set. | |
| task_uuid | Yes | The ticket's uuid, from list_tickets, get_board or search | |
| description | No | Replaces the whole body, Markdown, up to 20000 characters — it does not append. To add a line, read the current description with get_ticket and send it back with your addition included. | |
| label_uuids | No | The ticket's labels, as the complete set after the write — it REPLACES rather than adds, so include the ones already on the ticket or you remove them. Bare uuids, which is the shape get_board returns as `label_uuids`; get_ticket returns expanded objects, so take the uuid out of each. An empty array removes every label. | |
| status_uuid | No | Move it to this column, by uuid, from get_board. The exact form of `status`, unaffected by a column being renamed. | |
| custom_fields | No | Custom field values, keyed by the FIELD's uuid — read them off get_ticket, a field's name will not work. REPLACES the whole set, so send the values already on the ticket alongside the ones you are changing. A key no field on that board has is dropped silently: the call still answers 200 and the value is simply absent, so read `custom_fields` back off the reply to confirm the write landed. | |
| assignee_uuids | No | The ticket's assignees, as the complete set after the write — it REPLACES rather than adds, so include whoever is already assigned. The uuids come from the board's `members`. An empty array unassigns everyone. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and excels: it discloses the 409 conflict behavior, whole-set replacement for labels/assignees/custom_fields, silent dropping of unknown custom field UUIDs, and the need to read back custom_fields to confirm writes. This is exemplary behavioral disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a dense paragraph with no wasted words, but it covers a lot of ground. It is appropriately sized for a tool with 12 parameters and many gotchas, though it could benefit from bullet points for readability. Still, every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (12 params, nested objects, no output schema), the description is remarkably complete. It explains concurrency, replacement semantics, silent failures, and even mentions that the reply carries stored custom_fields. It leaves few unanswered behavioral questions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds substantial value by highlighting the most critical param behaviors: version must come from get_ticket, description replaces rather than appends, and the replace semantics for label_uuids, assignee_uuids, and custom_fields. It goes well beyond the baseline by summarizing and reinforcing the schema details.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Change a ticket,' a specific verb and resource. It clearly distinguishes from sibling tools like create_ticket, move_ticket, and comment_on_ticket by implication, and the detailed context about versioning and replace semantics makes the tool's purpose unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for how to use the tool (version handling, replace semantics) but does not explicitly state when to use it vs alternatives. The schema's status parameter mentions 'move_ticket is the tool for a move alone,' but the description itself lacks that exclusionary guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
upload_ticket_attachmentA
Attach a file to a ticket — the way an agent puts evidence on a ticket rather than describing it. Two ways to give it the bytes, and exactly one must be used. file_path reads a file from the machine THIS SERVER runs on (normally the agent's own machine, since the client starts this as a subprocess) and is the right one for anything that already exists on disk; it costs no context, so prefer it. text is for content the agent has just written — a log, a CSV, a diff — and needs filename alongside it. Laver refuses anything that is not one of application/pdf, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.openxmlformats-officedocument.wordprocessingml.document, image/gif, image/jpeg, image/png, image/webp, text/csv, text/plain, and refuses a file whose BYTES do not match the type its name claims, so renaming a zip to .png fails at the server rather than here. 25 MB is the ceiling. A workspace over its storage quota is a 402, which no retry fixes. Uploading is a write: a read-only role is a 403.
| Name | Required | Description | Default |
|---|---|---|---|
| text | No | Literal file content, for something the agent wrote rather than something on disk. Requires `filename` | |
| filename | No | The name to store it under. Required with `text`; defaults to the basename of `file_path`. Its extension decides the content type | |
| file_path | No | Path to an existing file on the machine running this server. Mutually exclusive with `text` | |
| task_uuid | Yes | The ticket's uuid, from list_tickets, get_board or search | |
| content_type | No | Overrides the type guessed from the filename's extension |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and does an excellent job. It discloses write semantics, server-side MIME type validation, byte-content matching enforcement, a 25 MB limit, 402 quota behavior with no retry expectation, and the context cost of file_path.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place by adding constraints, usage preferences, or error semantics. It is front-loaded with the purpose and structured logically from usage modes to validation rules to failure cases.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers use cases, constraints, and failure modes thoroughly, but it does not state what the tool returns on success (no output schema exists). It also omits mention of the content_type parameter in prose, but the schema covers it. These are minor omissions in an otherwise complete description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds significant meaning: the distinction between file_path and text, the requirement that text needs filename, filename's default from file_path, and how the extension determines content type. It does not mention the content_type override parameter in prose, but the schema already describes it, so this is only a minor gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses specific language ('Attach a file to a ticket') and clarifies the tool's role as putting evidence on a ticket. It clearly distinguishes this from sibling tools like list_ticket_attachments, get_ticket_attachment, and delete_ticket_attachment.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance for each parameter mode: file_path for existing files on disk with a preference expressed ('costs no context, so prefer it'), and text for agent-written content needing filename. It also notes the 403 for read-only roles, implying when not to use the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
TDQS
Each tool targets a distinct resource (ticket, board, wiki, automation, workspace) and a distinct action (list, create, get, update, move, etc.). Even similar tools like list_tickets and get_board are clearly separated by their descriptions. There is no ambiguity about which tool to call for a given operation.
Tool names follow a consistent verb_noun pattern throughout, e.g., list_tickets, create_ticket, update_ticket, get_wiki_page, restore_wiki. The only minor deviation is 'search' and 'comment_on_ticket', but the pattern remains predictable. No mixing of case or conventions.
With 30 tools, the surface is large, which could overwhelm agents. However, the server covers a broad domain (tickets, attachments, wiki, automations, boards) and each tool appears purposeful. It's on the heavy side but not extreme for the scope.
Ticket and attachment operations are fully covered, including archive/delete and comments. Wiki pages support create/append/versioning but intentionally not edit/delete. A notable gap is automation rules: they can be created and listed but not updated or deleted, which is a missing lifecycle.
Maintenance
Related MCP Connectors
Task & board management for AI agents + humans. Kanban, comments, digests via MCP.
Shared project memory for teams: read projects and tasks, write updates and wiki decisions.
Kanban board for teams and coding agents: manage tasks, subtasks, sprints and wiki pages via MCP.
Minimal project management for teams and AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to manage projects, epics, and tasks with atomic locking, real-time dashboard, and multi-agent coordination.MIT
- FlicenseNot gradedqualityBmaintenanceLightweight project management for teams and AI agents.
- AlicenseBqualityDmaintenanceEnables AI agents and humans to collaboratively plan and manage tasks with a shared kanban and dependency graph, all stored locally.312677MIT
- FlicenseNot gradedqualityBmaintenanceA local-first Kanban system with MCP tools for AI agents to manage tickets, features, and tasks, paired with a React UI for human users.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Developyn/laver-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server