Yarnt MCP Server
The Yarnt MCP server is the agent twin of the REST API: the same operations and permission model, exposed as MCP tool calls instead of JSON over HTTP. Point Claude, or any MCP client, at it and it can list, create, and move tickets the same way you do.
These are the tools. For the loop they are meant to drive, an agent that works its assigned queue until it is empty and then halts, see the /yarnt skill.
Server URL: https://mcp.getyarnt.com. Paste this bare
origin into your client. No path suffix.
Connecting
Transport is streamable HTTP, stateless: a client POSTs to the base URL for
every call. There's no SSE stream to keep open; GET and DELETE
against the URL both return 405.
Most clients only need the URL, since they discover the OAuth endpoints themselves. For Claude Code:
claude mcp add --transport http yarnt https://mcp.getyarnt.com
Other MCP clients take the same URL in whatever "add server" UI they have.
Authorizing
The first call opens a browser for a standard OAuth flow:
-
You land on
https://app.getyarnt.com/consent, signing in to Yarnt first if you aren't already. It reuses your normal web session, so there's no separate MCP login. - The consent screen says the agent wants to act in your workspace as a robot: a least-privilege workspace member. Pick an existing robot from the "Act as" dropdown, or type a name and click Add to create one right there. You do not need to pre-create anything in Settings.
- The scope shown is
tickets, the only scope there is. - Click Authorize; the browser returns to the client and the agent is connected.
Why a robot
The agent never holds your password or a service key, only a scoped, revocable token bound to the robot. It sees and does exactly what that robot's membership permits. Every call goes through the same row-level security and limit rules as the app; it cannot touch billing.
Tokens & revoking
Access tokens last 1 hour. Refresh tokens last 30 days and rotate on every use: each refresh issues a new one and revokes the old. Clients refresh automatically. Revoke access by disabling the robot or revoking its refresh token.
Auth is OAuth 2.1 with PKCE (S256, required). Dynamic Client Registration is supported, so clients self-register with no manual setup.
Limits that apply to agents
A robot is exempt from the in-flight (WIP) cap: starting a ticket assigned to a robot never fails on WIP, and a robot's in-flight work never eats into a human's cap. Robots are not paid seats and do not inflate the workspace queue pool.
The workspace queue cap still applies. An agent creating tickets can hit a full queue like anyone else. The free exits are the same as for anyone: finish something or drop something.
Tools
Tools return plain text rather than JSON. The one exception is get_attachment,
which returns an image when the attachment is one. Assignee values are user ids (from
list_users or whoami), never emails.
| Tool | Parameters | Does |
|---|---|---|
whoami | none | Your own id name (kind) plus the person who granted you access. Use instead of guessing which list_users row is you. |
list_tickets | list opt (now / next / queue default / wishlist / finished / all), assignee opt (me / unassigned / user id), project opt | Tickets in priority order; each row shows its list. |
get_ticket | id req | Full detail including the body/description. |
create_ticket | title req, project opt, wish opt bool | Adds to the queue's next list; wish: true files it on the wishlist instead, spending no queue slot. |
update_ticket | id req, title opt, body opt, assignee opt/nullable, project opt/nullable | Edit fields; omit to leave unchanged, null to clear. To move between lists use start/finish/drop/unstart. |
start_ticket | id req | Start it. |
finish_ticket | id req | Finish it. |
drop_ticket | id req | Drop it. |
unstart_ticket | id req | Put a started ticket back: state todo, top of Next, blocked and its reason cleared. The way an agent hands back work it cannot finish: comment the reason first, since unstarting clears it. |
list_users | none | Members you can assign to: id name (kind), kind is person or integration. |
list_projects | includeArchived opt bool | Project names; archived omitted by default. |
add_comment | ticketId req, body req | Comment on a ticket. |
list_comments | ticketId req | Comments, oldest first. |
list_history | ticketId req | Change history, oldest first. |
list_attachments | ticketId req | Files on a ticket, newest first: id name (size, type). |
get_attachment | id req, as opt (auto default / link) | Retrieve one. Images come back as an image block a vision model can look at; text files as their text; anything else, or anything over 4 MB, as a description plus a download URL signed for an hour. as: "link" always returns just the URL. |
add_attachment | ticketId req, name req, data req (base64), contentType opt | Attach a small file by value — a short log or diff the agent writes out itself. |
drop_attachment | id req | Remove a file from a ticket, bytes and all. For cleaning up a mistaken upload; it cannot be undone. |
start_upload | ticketId req, name req, contentType opt | Begin attaching a file on disk. Returns a URL to PUT the bytes to, a handle, and the upload command to run. The type is taken from contentType, or from the filename when that is left out. |
finish_upload | upload req (the handle) | Attach what was uploaded. The size is read back from the file that actually arrived; the type is the one declared at start_upload. |
Attachments
A file the agent produced goes up in two steps, and this is the way that works. Base64 of a
real screenshot runs to hundreds of thousands of characters, which no language model emits
exactly — and one wrong character corrupts the file. So start_upload hands back a
signed URL, the agent's own shell sends the bytes, and finish_upload records what
landed. The file never passes through the model.
start_upload → curl -X PUT -H "Content-Type: image/png" --upload-file shot.png "<url>" → finish_upload
start_upload prints that command with the URL filled in, and the header is part of
it: curl --upload-file declares application/octet-stream unless told
otherwise, and a screenshot recorded under that type is one nobody can view inline. So the type
is settled when the upload starts — from contentType, or from the filename — and
finish_upload records that rather than whatever the transfer happened to say.
The caps are your plan's, the same ones the web uploader enforces: per-file size, files per
ticket, and total workspace storage. Passing one comes back as a plain sentence saying which.
drop_attachment removes a file the agent should not have attached — the wrong one,
or a duplicate — so a mistake is not permanent.
Sample output
list_tickets:
1234abcd... [now] Fix the flaky deploy step @Claude
5678cdef... [next] Write onboarding empty states (unassigned)
whoami:
abcd1234-... Claude (integration)
granted by: dcba4321-... Ada
Questions about the MCP server: help@getyarnt.com.