Agents and the API
Two doors, because an agent and a service are not the same visitor.
An agent is a person at a laptop, acting on their behalf and needing their consent. A service is not. Kanso gives them different doors rather than one door with a note about how to use it.
Which door
An agent comes through /api/mcp: it registers itself, you approve it in your browser, and it holds a token that opens that one endpoint and nothing else. A service comes through the REST API with a token you minted for it, which is the whole API and carries your role. The first is for something acting as you; the second is for something acting on its own, and the difference is worth keeping.
The MCP door
One command, run wherever the agent lives:
claude mcp add --transport http kanso http://localhost:8080/api/mcp
Nothing is pasted. The command only writes down an address. The first time the agent calls it, Kanso answers 401 with a header naming its own authorisation server, and the agent registers itself, opens your browser and asks.
What you read there is a Kanso page on Kanso's own origin: which application is asking, where it would send the code, how old its registration is, and the permissions in sentences rather than scope names. Approve it and the agent holds a token.
That token opens one door and not the rest of the API — it is accepted at /api/mcp and refused everywhere else — and what comes through it acts as you: every change says your name in the activity feed.
Settings → Agents lists what you have let in and revokes it. Revoking takes the tokens with the permission, so the next request is refused rather than the next hour.
Sign in to Kanso in that browser first. The authorisation URL answers a browser with no session 401 with an empty body, so an agent's link opens on a blank page rather than on a login screen. It is the one rough edge left in this flow, and it is written down rather than smoothed over: signing in first makes it invisible.
Ten tools go through it
Three read, five write, and two are the triage pair — which is the one an agent is actually good at.
- kanso_list_tickets
- The tickets matching a scope and a filter.
- kanso_get_ticket
- One ticket in full, by identifier.
- kanso_team_workload
- What every member of a team is carrying, heaviest first: open tickets, how many are in progress, the points somebody sized, how many carry no estimate at all, and the age of their oldest open ticket.
- kanso_create_ticket
- File one ticket.
- kanso_update_ticket
- Change one, by identifier.
- kanso_split_ticket
- Turn one existing ticket into a parent and file the parts you name underneath it, in one call. Returns the identifiers the parts were given.
- kanso_link_tickets
- Record an edge between two tickets that already exist.
fromandtoread as a sentence:fromblocksto, duplicatesto, relates toto. - kanso_plan
- File a whole plan already worked out — several tickets, which are parts of which, and which block which — into one team, in one call.
- kanso_triage_queue
- What nobody has ruled on yet.
- kanso_triage
- The ruling:
accepted,backlogged,duplicateorclosed, recorded as a decision rather than applied as a silent edit.
Each of them speaks the team's vocabulary. A status key the ticket's team does not have is refused with the list of the ones it does, so an agent that guessed done at a team that calls it Livré is told, not ignored.
Dev mode has no door at all, and says so instead of failing quietly: under KANSO_AUTH_MODE=dev identity comes from an unverified header, and an authorisation server behind that would issue durable tokens to anyone who can reach it.
API tokens, for a service
Settings → Tokens mints one — kanso_pat_…, shown once, scoped kanso:read and kanso:write — and it is presented the ordinary way:
curl -H "Authorization: Bearer $KANSO_TOKEN" http://localhost:8080/api/teams
That is the whole REST API, not one door onto it. The token carries its owner's role, so an integration given an owner's token can do what an owner can. There is no scope narrower than a person today, which is worth knowing before handing one to somebody else's software.
Webhooks
With a token, an integration provisions itself: POST /api/webhooks subscribes and returns a signing secret. Every delivery afterwards carries two headers.
- X-Kanso-Event
- What happened.
- X-Kanso-Signature
t=<unix>,v1=<hmac>over the body, with the secret the subscription returned.
An integration can also declare a custom field on a team, write and read its values, and — importantly — ask /api/teams/{id}/statuses and /api/tickets/filters what words this instance actually uses. A team invents its own, so a service that hardcodes done is a service that breaks on the team that renamed it.
The API describes itself
GET /v3/api-docs, behind a session or a token like every other read. It is generated from the controllers rather than kept beside them, so it says what the code says: 153 paths and the shape of every request body — which is the half a route list never gives.
GitHub
Kanso knows what the work is; GitHub knows whether it is done. Without a link between them the same fact gets typed twice — a branch named after a ticket, then a ticket dragged to Done after a merge — and the second typing is the one that gets forgotten.
An owner registers a GitHub App for the instance; each member then links their own GitHub account from settings, which is the one card on that screen a plain member has something to do on. A pull request whose branch or body names a ticket key shows up on that ticket, with the branch to start it from.
Linking your account is also what decides how the feed reads. With it, the activity says you moved KAN-142 to Done; without it, the same event reads KAN-142 moved to Done via #418.