Skip to main content
Glama

@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

list_workspaces

Where to start when you have no uuids

list_boards

The boards in a workspace

get_board

A board with its status columns, labels, members and tickets

list_tickets

Tickets on a board, filterable, paged — updated_since is how you follow a board

list_workspace_tickets

Triage across every board at once — overdue, unassigned, or free text

get_ticket

One ticket in full, its subtasks, including its version

get_ticket_comments

Comments and activity history

get_ticket_flow

How long the ticket has spent in each column

list_custom_fields

A board's custom field definitions — the uuids update_ticket writes against

list_labels

Every label in the workspace, not only the ones already used on one board

search

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

get_board(include_tasks: false)

the structure alone — statuses, labels, fields

155 kB → 17 kB

get_wiki_tree(depth: 1)

the top level, nothing nested under it

119 kB → 1.8 kB

get_wiki_tree(parent_page_uuid: …)

one page and everything beneath it

119 kB → 9.8 kB

list_tickets(include_descriptions: false)

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

create_ticket

status takes the column name, or pass status_uuid; markdown in description is parsed

update_ticket

Needs version; markdown in description is parsed; custom_fields is keyed by field uuid and replaces the whole object

move_ticket

Needs version, and a column — neither column is a 400

comment_on_ticket

Markdown in body is parsed; no version, so it cannot 409

update_comment

Your own comments only; replaces the whole body and is marked as edited

delete_comment

Your own comments only; to the trash, and nothing here restores one

mark_comments_read

Clears this ticket's unread badge for the user the key acts as

add_subtask

One checklist item, appended — plain text, not markdown

update_subtask

Tick it off (is_done), rename it, or move it up the list

delete_subtask

Not recoverable — a checklist item has no trash

archive_ticket

To the trash — recoverable for 30 days

delete_ticket

Destroys one already in the trash — permanent

create_board

Optionally from a template — crm or sales-leads

link_tickets

"this before that" — direction is blocks or blocked_by

unlink_tickets

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

create_status

A new column, appended to the right-hand end

update_status

Rename, recolour, or set is_complete — a board has at most one

reorder_statuses

The complete list of uuids, in order; a stale list is a 409

delete_status

The column must be empty, and a board keeps one

create_group

A swimlane; there is no rename route, so a wrong name is deleted and remade

reorder_groups

Same complete-list contract as reorder_statuses

delete_group

Tickets in it survive, ungrouped — and their versions all move

create_custom_field

Workspace owner or admin; type cannot be changed afterwards

update_custom_field

Rename, reorder, or replace a select's whole options list

delete_custom_field

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

list_labels

Workspace-wide, alphabetical — get_board shows only what a board uses

create_label

A hex colour is required; names are unique ignoring case and space

update_label

Renames it everywhere — one label, not a copy per board

delete_label

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

list_ticket_attachments

Name, type, size and uuid — never the bytes

get_ticket_attachment

Text inline, an image as an image block, anything else via save_to

upload_ticket_attachment

file_path for a file on disk, or text + filename for something written

delete_ticket_attachment

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 ![alt](https://…) 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

list_automations

The rules on a board, each with its version

get_automation

One rule in full — the conditions and actions an edit replaces

list_automation_runs

What a rule has actually done, newest first

create_automation

Owner or admin only — and see below before calling it

update_automation

Needs version; enabled: false is the reversible stop

delete_automation

Needs version, and takes the rule's whole run history with it

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_runs afterwards 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_automation with enabled: false stops a misbehaving rule at once and keeps its history; deleting destroys the history along with the rule. conditions and actions replace the stored lists rather than merging into them, so build them from get_automation and 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.js installs session_only on 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_tickets covers the triage half and is the closest substitute meanwhile.

  • Listing subtasks on their own. Needs no tool rather than lacking one: get_ticket already 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_board names 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_attachment already 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_tickets is the better answer to what to pick up, and search to 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_flow covers 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_board returns the board's wiki_page_uuid, and get_wiki_page and get_wiki_tree read 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 manual rule 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.js installs session_only on the plugin, so a key gets a 401 from every route there — POST /boards/:uuid/sprints included. 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, by restore_wiki, now that list_wikis can 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_page and update_wiki_page put 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 wants get_wiki_page.

  • Imports and feedback forms.

Not ever, from a key:

  • Reporting a deployment. POST /tasks/:task_uuid/deployments is 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, so get_ticket_flow and the timeline carry what happened, when, and which key said so.

  • Reporting a merged pull request. POST /tasks/:task_uuid/pull-requests/merged is 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 has move_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, so get_ticket_flow carries 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_links answers 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_page already 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_boards deliberately 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, because get_board already sends each column's entry_requirements and a refused move_ticket names 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-workspace names 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/exports and 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/archive bins 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_tickets with updated_since is 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 back

delete_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-api

Two 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_URLhttps://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.tool

For 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 --otp saves a prompt from failing a non-interactive run; drop it if not enrolled.

  • npm whoami answering with that account — npm login if 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 ci deletes and reinstalls mcp/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/mcp

A 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 tools
append_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.

ParametersJSON Schema
NameRequiredDescriptionDefault
page_uuidYesFrom get_wiki_tree or a search_wiki hit. A wiki uuid passed here is a 404, not a page
content_markdownYesAppended after everything already on the page. Longer than 100k characters is refused rather than truncated

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
task_uuidYes

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
bodyYes
task_uuidYesThe ticket's uuid, from list_tickets, get_board or search

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
actionsYesEach action is an object with a `type` from the list and whatever that action needs — a status, a user, a label, a comment body
enabledNo
run_limitNocolumn.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_uuidNocolumn.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_uuidYesThe board's uuid, from list_boards
conditionsNoA flat AND — every condition must hold. No OR and no nesting. Omit for every occurrence of the trigger
trigger_typeYes
trigger_configNoRequired 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_uuidNoDefaults to the user this key acts as. Read the warning above

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
templateNoOmit for the default columns
workspace_uuidYes

TDQS

A4.7/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
titleYesThe 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.
statusNoThe 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_dateNoDue date as an ISO 8601 date, e.g. 2026-08-14
positionNoSort 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.
priorityNoOne of low, medium, high or urgent. Left off, the ticket simply has no priority set, which is not the same as low.
board_uuidYesThe board to create it on, from list_boards
descriptionNoThe body, Markdown, up to 20000 characters. Headings, lists and code fences all render.
status_uuidNoThe 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

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 ![alt](https://…) 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.

ParametersJSON Schema
NameRequiredDescriptionDefault
titleYes
wiki_uuidYes
content_markdownNoThe page body as markdown. Omit for an empty page. Longer than 100k characters is refused rather than truncated
parent_page_uuidNoNest under this page. Must be in the same wiki — a page from another wiki is a 400, not a silent move

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
task_uuidYesThe ticket's uuid, from list_tickets, get_board or search
workspace_uuidYes

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
task_uuidYesThe ticket's uuid, from list_tickets, get_board or search
attachment_uuidYes

TDQS

A4.4/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
board_uuidYesThe board's uuid, from list_boards
include_tasksNoSet 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

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
task_uuidYesThe ticket's uuid, from list_tickets, get_board or search

TDQS

A4.7/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
save_toNoAbsolute path to write the file to, on the machine running this server. Parent directories are created. An existing file is overwritten
task_uuidYesThe ticket's uuid, from list_tickets, get_board or search
attachment_uuidYes

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
task_uuidYes

TDQS

A4.4/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
page_uuidYes

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
versionYesWhich save to read. 1 is the page as first created; get_wiki_page reports the current number
page_uuidYes

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
depthNoHow 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_uuidYes
parent_page_uuidNoReturn 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

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
board_uuidYes

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
workspace_uuidYes

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
task_uuidYes

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qNoFree-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.
limitNoTickets 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.
cursorNoThe `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.
statusNoA 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_uuidYesThe board to read, from list_boards
status_uuidNoA 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_sinceNoReturn 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_descriptionsNoSet 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

A4.9/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
archivedNoOmit 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_uuidYes

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNoTarget column name
versionYesThe `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.
positionNoSort 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_uuidYesThe ticket's uuid, from list_tickets, get_board or search
status_uuidNo

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
wiki_uuidYes

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qYes
wiki_uuidYes

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
titleNoReplaces the title. Omit to leave it as it is.
statusNoMove 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.
versionYesThe `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_dateNoDue 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'.
positionNoSort 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.
priorityNoOne of low, medium, high or urgent. Omit to leave it alone; there is no value here that clears a priority already set.
task_uuidYesThe ticket's uuid, from list_tickets, get_board or search
descriptionNoReplaces 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_uuidsNoThe 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_uuidNoMove it to this column, by uuid, from get_board. The exact form of `status`, unaffected by a column being renamed.
custom_fieldsNoCustom 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_uuidsNoThe 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

A4.5/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
textNoLiteral file content, for something the agent wrote rather than something on disk. Requires `filename`
filenameNoThe name to store it under. Required with `text`; defaults to the basename of `file_path`. Its extension decides the content type
file_pathNoPath to an existing file on the machine running this server. Mutually exclusive with `text`
task_uuidYesThe ticket's uuid, from list_tickets, get_board or search
content_typeNoOverrides the type guessed from the filename's extension

TDQS

A4.8/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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

A4.4/5.0
Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count3/5

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.

Completeness3/5

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

ActivityMaintained
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

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