MCP tools
The tools knotless-mcp offers over stdio, and a public knotless-serve
offers at /mcp. They name a repository as handle/name. Reads answer from
a local cache mirror, writes go through the same code as the CLI, and
errors come back as tool errors for the agent to read. One a PDS refused
for want of scope, as a login from before knotless asked for more is, adds
that the user signs in again: reconnecting knotless in their assistant, or
running knotless login for a knotless on their machine. One the login
asked for, but its PDS did not grant, says so instead: for a write in a
space, that the PDS granted the collections the space type’s lexicon
listed when the user signed in, and that signing in again grants this one
once knotless’s lexicon lists it.
On a server, each account’s login with its PDS can end while its assistant’s
token still works: a PDS ends a session after a lifetime of its own. Once the
PDS turns down refreshing it, the server forgets the session, answers the
next call 401 and the next token refresh invalid_grant, so the assistant
has its user sign in again. A refresh that fails any other way is logged,
and the call goes on.
Agents says how to connect a client. Pulls, issues,
diffs and files are resources to read by URI too.
Over stdio, knotless-mcp asks its appview about versions once a day, as
the CLI does (Command reference). With a newer release
out, the first tool’s answer ends with a text saying so. Older than the
appview asks for, every tool fails, saying why and to run knotless upgrade, unless KNOTLESS_IGNORE_MINIMUM is set. /mcp on a server runs
that server’s own build and does neither.
The tools are the commands
Section titled “The tools are the commands”Every command knotless <group> <action> has one tool, <group>_<action>,
with - as _: knotless repo star is repo_star, knotless pull update-branch is pull_update_branch. A tool takes what its command
takes. A parameter is the flag’s name with - as _ (--keep-branch is
keep_branch, --undo is undo), and the positionals are named as the
command’s usage names them (repo, uri, name). Where a command has an
undo, the tool takes undo: true. A verb that works on a pull or an issue
exists as both tools (pull_close, issue_close). Every tool is a
command, and nothing is on the MCP alone.
The commands with no tool act on the machine they run on: login,
logout, upgrade, repo mirror, workflow pin and release download.
The command reference has each command’s flags and what it
prints; this page has what a tool takes beyond that and what it answers.
The tools are listed by group, as the commands are. Actions
lines them up against the commands, the methods and the pages.
knotless-mcp --read-only offers only the tools that read, as status,
repo_list, repo_refs, repo_tree, repo_read, repo_blame,
repo_search, repo_log, repo_diff, pull_diff, pull_show,
issue_show, pull_list, issue_list, run_list, workflow_list and
release_read (READS in crates/knotless-mcp/src/lib.rs lists them
all), and runs no other, whatever login it has: for an agent that may read but must change
nothing, as the review agent is.
status
Section titled “status”statusWhat it does a
knotless-serve’s health, from/_status, forserver(the appview by default), withrepo(handle/name) its mirror too
account
Section titled “account”account_profileWhat it does how you show on knotless, over your Bluesky profile, and, with
bot, whether the account is a bot, which the feed leaves outaccount_email,account_email_add,account_email_removeWhat it does
account_emailsays how many addresses you claim,{claimed};account_email_addclaims anaddressas yours andaccount_email_removetakes it back, each answering{changed, claimed}, how many you claim nowaccount_followWhat it does follows a
handleon knotless, or withundounfollows it, answering{following, uri}account_follows,account_followersWhat it does
account_followslists whom ahandlefollows, yours by default, as{follows: [{handle, uri, createdAt}]};account_followerswho follows it, from the appview, as{followers: [{did, handle}]}account_searchWhat it does the accounts owning public repositories whose handle holds
text, as the appview’s search finds them, at mostlimit(10 by default, at most 50), as{owners: [{owner: {did, handle}, repoCount}]}account_delegateWhat it does hands your writes to a gateway (the appview’s, by default) so collaborators can push, approved in the browser
account_delete_dataWhat it does without
confirm, what knotless data your account holds, asgetAccountDataanswers it; withconfirm, your handle, deletes it all, answering{deleted, loggedOut}
repo_list,repo_refs,repo_tree,repo_logWhat it does read a repository, an account’s repositories, a directory (
path,rev) or the history (rev,path,limit, 20 by default)repo_readWhat it does a file at
rev, optionally only linesstarttoend(long files)repo_searchWhat it does the lines holding a fixed string, in any case; see the parameters below
repo_blameWhat it does who last changed each line of
pathatrev(the default branch if not given), asgit blamefinds it in the cache mirror: runs of lines, each{sha, start, count, author: {name, email, date}, summary, previous},startcounted from 1,datein UTC, andprevious, where the commit’s parent had the file, that parent and the file’s path thererepo_diffWhat it does what
headchanged sincebase; answersNo differences: …whenheadchanges nothing sincebaserepo_storageWhat it does what a repository takes up on its PDS: its packs, their objects and bytes, and the bytes in blobs
repo_compactWhat it does as the owner, fold a repository’s older packs into one and drop old claims and deleted refs’ leftover records, answering
{before, removed, claims, refs}repo_commitWhat it does change files on a branch, optionally against a
basecommit, and withbatch, as the owner, through the gateway; see the parameters belowrepo_createWhat it does takes
links([{url, title?}], its website first) and adefault_branch(mainif not given) and names therunnersgiven (none, for an empty list), or else the appview’s default runner, and returns them asrunners: a private one’s are members, invited, with awarning(null otherwise) if one could not be. It answers thememberswho may push to a new repositoryrepo_editWhat it does change a repository’s description (empty drops it), its
links([{url, title?}], which replace those it had; empty drops them), itstopics(names, which replace those it had; empty drops them), and the branch its HEAD names, one of its branches once it has any, answering{description, links, topics, defaultBranch}repo_renameWhat it does rename; the old name redirects
repo_archiveWhat it does makes one you own read only, or with
undowritable again, answering{archivedAt}(null once unarchived)repo_deleteWhat it does delete a repository you own and everything in it, with what you wrote about it;
confirmis its name again, or it refusesrepo_transfer,repo_acceptWhat it does offer a public repository to
to, or withundowithdraw the offer; take one offered to yourepo_forkWhat it does copy a repository into your account, under
nameif givenrepo_shareWhat it does let
handleread a private repository, or withwritepush too, which first delegates your writes to the gateway when you have not, approved in the browser asaccount_delegateis, then answers who may read and write it and, as a second text,Delegated your writes to the gateway <did> first.; withundotakes access awayrepo_membersWhat it does who may read and push
repo_star,repo_starsWhat it does
repo_starstars a public repository, or withundotakes the star back, answering{starred, uri};repo_starslists the repositories ahandlestarred, yours by default, as{stars: [{repo, uri, createdAt}]}
Parameters beyond a flag’s name:
repo_commiteach
{path}with one ofcontent,base64,editsordelete: true;editsis a list of{old, new}made in turn in the file as the branch holds it, eacholdthere exactly once, the file keeping its mode. A miss failsedit <n> of <path>: old is not in the file, or says how many times it is there; a path with no regular file fails<path>: no file there to edit. On a branch that moved, edits are made in the file as it is there (long files)repo_searchonly files under
path, a directory, file or glob; withcontext, runs of lines around the matches instead of lines, each{path, start, end, matches, text},matchesthe line numbers that match andtextthe run’s linesrepo_readonly those lines of a text file, from 1,
endby default its last, as{start, end, lines, text},linesthe file’s countrepo_blameonly the runs for those lines,
endby defaultstart
branch and tag
Section titled “branch and tag”branch_create,branch_delete,branch_renameWhat it does make, remove or rename a branch; a rename takes the open pulls on it along
branch_protectWhat it does holds collaborators to a branch’s rules, or with
undodrops them. Takesmerges,queue,reviews(how many approvals, 0 to 10),code_owners,dismiss_staleandgates(handles or DIDs whose review of a pull’s head it waits for, see gates); into such a branchpull_mergerefuses a pull not reviewed as it asks, naming what is missing, unless the owner forces it (see required reviews)branch_rulesWhat it does lists the rules, a rule that asks for code owners with
warningswhen CODEOWNERS on its branch gives you, the owner, a path alone (your own pull changing it never has a code owner’s approval, as an author’s never counts, and merges only by force); showsqueuetag_create,tag_deleteWhat it does make or remove a tag, such as a release’s version, at a commit, branch or tag
pull and issue
Section titled “pull and issue”pull_create,issue_createWhat it does open one, following the repository’s template: they refuse one with no body when there is a template.
pull_createtakesbase,head_repo,head_branch,title,body,images,draftandreviewerspull_template,issue_templateWhat it does what a repository asks a pull’s or issue’s title and body to follow: read before
pull_createorissue_createpull_list,issue_listWhat it does takes a
queryin GitHub’s search words (collaboration.md), and says of each pulldraft,trueorfalsepull_show,issue_showWhat it does a pull or issue and its comments; on a pull, what it brings in and whether it merges cleanly, and on an issue
mentionedBy, the pulls whose title or body names it, oldest first, each{pull, closes},closestrue where it names it after a closing keywordpull_diffWhat it does the diff a pull would apply, numbered as
comment_add’slinestake thempull_edit,issue_editWhat it does change your pull’s or issue’s title and text;
pull_editalso whether your pull is a draft (draft,ready) and whom it asks to review it (reviewers,no_reviewers)pull_close,pull_reopen,issue_close,issue_reopenWhat it does change an item’s state: its author, the owner or a collaborator; refuses an item that is not there, a merged pull and a state it is in already
pull_label,issue_labelWhat it does put labels on a pull or issue as owner, collaborator or triager, or with
undotake them offpull_assign,issue_assignWhat it does assign a pull or issue to accounts, or with
undotake them off; as in collaboration.md, the owner and collaborators assign. Answersassigneesby DIDpull_ask_reviewWhat it does ask accounts to review a pull, or with
undostop asking: the owner and collaborators, and a pull’s author in the pull. Answersassigneesandreviewers, by DIDpull_dismiss_reviewWhat it does dismiss a reviewer’s standing review of a pull, saying why: a comment naming it, so it counts for nothing, for or against, until they review again. As in collaboration.md: the owner and collaborators may, on their own pulls too. Answers the comment’s
uripull_react,issue_react,comment_reactWhat it does reacts to a pull, issue or comment (
uri) onrepo(in its space, for a private one) with acontentamong GitHub’s eight, or withundotakes it back, answering{reacted, uri}pull_retargetWhat it does point a pull at other branches
pull_revertWhat it does undoes a merged pull (
uri) in one commit onrevert/<rkey>and opens a pull of it into the same branch, sayingreasonif given, answering{pull, branch, commit}(reverting a pull)pull_update_branchWhat it does merges an open pull’s (
uri) base into its branch and pushes it, answering{branch, commit, added},commitabsent when the branch held its base already, andaddedthe paths where both sides only added lines and the base’s come first (updating a pull’s branch)pull_mergeWhat it does merge a pull, queue it or check it; see below
pull_create,issue_create,pull_edit,issue_edit,comment_add,comment_editAnswer pictures to show after the body, each
{base64, alt}, a PNG, JPEG, WebP or GIF: uploaded, made smaller when over 1 MB, and attached (collaboration.md); with an edit, only beside abodypull_createAnswer open it as a draft, which neither merges nor queues until marked ready; ask these accounts, by handle or DID, to review it (drafts and requested reviewers)
pull_editAnswer for your pull:
draftmakes it a draft,readymarks it ready; ask more accounts to review it, or stop asking somepull_list,issue_listAnswer also
is:draft,-is:draft,review-requested:<handle, did or @me>,assignee:<handle, did or @me>andno:assigneepull_diffAnswer the pull’s unified diff, or with
pathsonly those files’ parts of it (for a merged pull, what its merge brought in, from the commits its state records, so it answers once its branch is gone), each line of a hunk led by its line in the old file and in the new (blank on the side it is not on) and|, ascomment_add’slinestakes them: a+line isside: newat its new number, a-lineside: oldat its old number, an unchanged lineside: newat its new number. File headers,@@lines and\ No newlineare not numbered. A diff longer than 60,000 bytes so numbered, asked for withoutpaths, answers insteadsaidandfiles, each file’spath(a renamed file’s new one),added,removedandlines, to ask for bypathsa few at a time, since an MCP client cuts off an answer that longpull_show,issue_showAnswer
item.draft,trueorfalsefor a pull, anditem.reviewers, those it asks to review it,{did, handle, pending},pendingwhile the pull is open and they have not reviewed it, andfalseonce it is merged or closed; for a pull,item.reviews: each reviewer’s standing review,{by, handle, verdict, commit, uri, outdated, maintainer, uncounted},outdatedonce the open pull has moved pastcommit,maintainerfor the owner or a collaborator, anduncounted, when its branch asks for reviews, why an approval does not count; on each comment, its author’shandleandfacets, the accounts it mentions; on a comment on lines,line({path, side, line, startLine},sidenewandstartLinethe same aslinewhere its record leaves them out) andreview, which a review’s own comment names too; on each comment of a thread on lines, and on no other,resolved; on a review its reviewer’s later one stands in place of,superseded: true; and on an answer,reply;reactions, on it and on each comment, as{"<content>": count}or null, as the appview counts them, or for a private repository as its space has them; for a pull, alsocompare, which for a merged pull is what its merge brought in (basewhere the branch was before it,headthe merge), whether or not its branch is still there; for a merged pull a merged revert undid,item.revertedBy, that revert, and on a revert,item.reverts, the pull it undoes; for an open pull in the queue or ejected from it,standing:{said, place, batch}
pull_merge
Section titled “pull_merge”pull_merge takes wait (seconds, at most 300), force and keep_branch, as
the CLI’s pull merge takes --wait, --force and --keep-branch. Merged, it
answers {sha, method, deleted, kept, closed}: the commit its branch is
at, how (merge or squash), the head branch it deleted,
or else why it kept it (asked to, in another repository such as a fork,
the branch merged into or the default branch, or named by another open
pull), and the issues it closed.
With queue: true, on a branch protected with queue, it puts the pull
in the merge queue instead and answers {"queued": {base, position, of}} at once, with
ejectedBefore, why, when it is queued again at the very head the queue
last ejected; it then
ignores wait, force and keep_branch. Its description tells an
agent to queue a pull once it builds, with the target branch merged in;
once ejected, to read why and the failed checks’ logs, fix it and queue
again, and to queue the same head again only when the failure is not the
pull’s; and to stop after a second ejection for the same reason.
Into a branch protected with queue (branch_protect takes queue, and
branch_rules shows it), pull_merge without force queues the pull instead,
answering {"queued": {base, position, of}, "why": "<base> takes pulls only through the merge queue"}; see queue-only
branches.
With auto: true, on a branch with no merge queue, it sets the pull to
merge by itself once its checks pass, and answers {"autoMerge": "set", "commit": <head>}; with undo: true as well, it cancels that, answering
{"autoMerge": "cancelled"}. pull_show then has autoMerge, where it
stands in words. See merge when checks pass.
With check: true, it merges
nothing and answers whether it would now, as knotless pull merge --check
says: {state, reason, conflicts, checks, method, deletes, base, head, may: {merge, force, queue, base, head, rerun}, queueOnly, reviews, reviewsMissing, reviewsUncounted, codeOwnersWarnings}: reviews is
passed, blocked, or none when the branch asks for none, and
reviewsMissing, reviewsUncounted (the approvals that do not count, and
why) and codeOwnersWarnings are [{text}]. may is what the caller’s
part in the repository lets it do, not whether that would work now:
state and reason say that, and a blocked pull’s merge is refused, the
owner’s too, unless the owner forces it. When the caller may not merge
the pull, reason says so too. Over MCP, a refusal for reviews names the
comment_add tool with verdict: "approve", and pull_merge with force, where
the CLI names its commands.
standing.said reads as Queued, 3rd of 5, Queued, 1st of 2, running its checks, Queued, 1st of 2, running its checks again on main as it is now, Queued, 1st of 2, but its test failed. The runner ejects it, or runs it again if that run was on an older main, Queued, 2nd of 2, ready for the next batch, In a batch of 4 being checked, Landing, or Ejected: <reason>, with the failed checks.
comment
Section titled “comment”comment_addWhat it does comments on a pull or issue, or reviews a pull: a
body, and averdict(approveorchanges) for someone else’s pull; a comment’s URI asurianswers it in its thread. Answers{uri, written}: the first it wrote and all of them, and for an approval from someone who may not write to the repository,note, that it does not count toward a protected branch’s reviewscomment_editWhat it does change your comment’s text
comment_deleteWhat it does take your comment or review back
comment_reactWhat it does as
pull_react, to a commentcomment_resolveWhat it does resolve the thread a comment on a pull’s lines starts, or with
undoopen it again: its author, the pull’s, the owner or a collaborator
comment_add takes lines, comments on lines of a pull’s diff at its head,
each {path, line, start_line?, side?, body} (side old for lines it
removes), each starting a thread: with a body or verdict, as that
review’s, all sent at once, otherwise on their own; at most 100.
label and triager
Section titled “label and triager”label_list,label_define,label_deleteWhat it does a repository’s labels: what it defines, defining one as owner (
name,color,description,exclusive), or taking one awaytriager_list,triager_add,triager_removeWhat it does the accounts whose labels count, naming one as owner (
handle,scopes) or dropping ittriager_evalWhat it does how an account’s labels agree with everyone else’s, per scope, as
[{scope, labelled, compared, agreed}]
queue_listWhat it does each pull waiting, in order, with its
placeand thebatchit is in while that is checked or landing, or thepreflightworkflows it waits for, then the open pulls ejectedqueue_leaveWhat it does takes a pull out,
{"left": <uri>}; the owner and collaborators may
run, workflow and runner
Section titled “run, workflow and runner”run_listWhat it does what the repository’s runners report of its workflows, waiting for runs to finish if asked; each log is its last 8000 bytes;
imagesreturns a check’s pictures, at most 12 at oncerun_logWhat it does the newest check that
ref,pullorqueue,workflowandcommitfind, givingworkflowwhen they find several: while it runs, what it printed so far as the appview holds it,{text, cursor, steps, going, ended, check},textits last 8000 bytes; once it finished,{text, check},textthe end of its log.checkis asrun_listgives itrun_rerunWhat it does as owner, ask the runners to run a ref or a pull again, or one workflow of either, with
envat a refrun_cancelWhat it does as owner, ask the runners to cancel a ref’s or a pull’s runs going or waiting, or one workflow’s,
{"asked": <uri>}workflow_listWhat it does the workflows at a
ref(the default branch), and what runs each there:{ref, workflows: [{name, push, pull, asked, schedule, error}]},schedulethe[{cron}]it runs on at the default branchrunner_list,runner_add,runner_removeWhat it does the accounts whose checks a repository shows; as owner, name or drop one: a private repository’s is made a member and invited, again each time it is added
runner_minutesWhat it does the minutes each of a repository’s runners counts for its owner’s public repositories in 30 days, against its allowance, and when minutes free up
agent_list,agent_add,agent_removeWhat it does the accounts whose pulls a repository’s runners check as they move, as code no one vouches for; as owner, name or drop one
secret_list,secret_set,secret_removeWhat it does the secrets a repository’s owner, or an admin of the organization that owns it, gives its runners:
secret_setsets anamewith itsvalue,refsto limit it to some refs’ pushes andrunnersto some of the repository’s runners;secret_removetakes one back. They answer each, one per runner,name,runner,refs,key,updatedAt, never a value. A value given here passes through the conversation: for one that should not, runknotless secret set <url> <name>with it on stdin
run_listonly the runs of pushes to that ref, in full, and those asked for there; a pull’s are found by
pull, a batch’s byqueuerun_listonly the runs of that branch’s merge-queue batches, by its short name (
main)run_listonly the runs of that commit: its hex ID, or at least its first 4 digits
run_listseconds to wait, at most 300, for a check to show and none to be queued or running; finding none by then is an error naming what was asked for
run_logthe
cursoran earlier answer gave: only what the run printed after it, all the appview holds without it or for one of another run of the checkrun_logseconds to wait, at most 300, for the check to show, then while it runs for it to print more after
since, asking the appview again 25 s at a time: it answers as soon as there is more, the run’s steps end, or the time is up
release
Section titled “release”release_yankWhat it does withdraw a
versionof apackagefrom a repository you own, or withundotake that back (yanking), answering{uri}, the release’srelease_publish,release_list,release_readWhat it does releases (packages.md):
release_publishpublishes aversionof apackagefrom a repository you own, itsfileseach{name, content}or{name, base64}, with optionalcommit,checkandnotes, answering{uri};release_listlists a repository’s, of onepackageif given, as[{package, version, latest, yanked, commit, check, notes, createdAt, files: [{name, size, sha256}]}];release_readreads onefileof aversion(the latest by default), checked, as{version, content}or{version, base64}, refusing a file over 1 MB
mod_hideWhat it does as owner, hide a pull, issue or comment, or with
undoshow it againmod_block,mod_blocksWhat it does
mod_blockleaves out what ahandlewrites about your repositories, or one, or withundostops;mod_blockslists the accounts an account blocks, yours by default, the latest first: eachdid,handle,repo(the one repository, by itsat://URI as when blocked, or null for all) andcreatedAtmod_reportWhat it does report to the appview
As the appview’s operator: an account its KNOTLESS_ADMIN_DIDS names.
admin_takedown,admin_status,admin_reports,admin_stats,admin_logsWhat it does take a subject down or, with
undo, put it back; whether it is taken down; the reports received; how the appview is used; the lines it logged
An organization’s members as you may see them, yours or an account’s
(org): the organization and its members see invitations and hidden
members, anyone else public members, as public: true shows them to
anyone.
| Tool | Command | What it does |
|---|---|---|
org_add | knotless org add | invites an account (admin: true for an admin) or changes a member’s role, keeping how it shows and when they joined, yours or, with org, one you are an admin of |
org_remove | knotless org remove | takes a member out or withdraws an invitation, yours or, with org, one you are an admin of |
org_join | knotless org join | with visibility (public or hidden) accepts an invitation or changes how it shows |
org_leave | knotless org leave | leaves |
org_members | knotless org members | the roster |
org_list | knotless org list | the organizations an account is a public member of |
org_delete | knotless org delete | ends yours |
They answer {org, name, organization, inside, gateway?, entries: [{did, handle?, role, invitedAt, expiresAt?, joinedAt?, standing}], changed}: changed null, or for org_add {org, who, role, was?, had?, first, private} (first when it made the account an organization, private when the invitation is in the organization’s space rather than its public records), for org_remove {org, who, was?, organization}, for org_join {org, role, visibility}, for org_leave {left, warning}, for org_delete {removed: [entries]}. An organization’s admin changes its repositories’ settings with repo_edit, repo_archive, repo_compact, branch_protect, label_define, label_delete, triager_add, triager_remove, agent_add, agent_remove, runner_add, runner_remove, repo_share and secret_set from their own login, through the appview; repo_rename and repo_transfer take the organization’s own.
notification
Section titled “notification”notification_listWhat it does what others did that concerns you, from the appview (a
mentionreason for a pull, issue or comment that names you as@handle,invitefor an organization’s invitation you have yet to answer, itsaboutthe role it offers, andchecks-failedandejectedfor checks failing at your pull’s latest run and the merge queue taking it out, from the runner, withchecksthose that failed, as{workflow, status, check}), the latestlimit(50 by default, 1 to 100), as{notifications: [{reason, author, about, repo, uri, subject, verdict, mentioned, lineComments, checks, new, createdAt}], markedSeen}:repothe repository ashandle/name, forpull_show;subjectwhat it is about, the pull or issue a comment is on, else the pull or issue itself, or for a star the repository’s record (null for a follow or an invitation);verdicta review’s;mentionedtrue when it names you though it is heard of for another reason, as a review of your pull that names you is heard of once, as acomment;lineCommentshow many line comments a review was sent with; andnewwhether it was new before this listing; marks them all seen after, unlesspeek, andmarkedSeensays it did, so the next listing has themnew: false.kindlists onlymentions,reviews,comments,pulls-and-issues,checksorstars-and-follows, and marks none seennotification_settingsWhat it does which notifications you hear of, kept by the appview:
onandoff, lists of reasons (star,follow,pull,issue,comment,mention,review-requested,invite,checks-failed,ejected,subscribed,watching), andfollowed_onlyoranyone, change them first; answers{reasons: [{reason, enabled}], followed_only}notification_watchWhat it does what you hear of a repository (
repo,handle/name) atlevelall(the default),releasesorignore;participatingforgets the choice, but is kept for your own repository, watched atalluntil you choose. Answers{subject, level, createdAt}notification_subscribe,notification_mute,notification_unsubscribeWhat it does what you hear of a pull or an issue (
uri): every comment, nothing but a review asked of you, or as your part in it says again. Answer{subject, level, createdAt}notification_subscriptionsWhat it does every choice, the latest first, as
{subscriptions}
theme_checkWhat it does a
palettechecked, answering{readable, shortfalls: [{text, on, ratio}]}, each text that would not read at 4.5 to 1 on what it is drawn ontheme_publishWhat it does publishes a
palettewith anameas adev.knotless.theme, answering{uri}, and withusedraws your pages in it; a palette that falls short is neither published nor usedtheme_useWhat it does draws your pages in a
theme(system,light,dark,latte,mocha,dracula,pckt,plyr,blacksky, a published one’sat://URI, orpalettewith apaletteof your own), answering{using}, the name of what they are drawn intheme_deleteWhat it does deletes one of yours by URI, answering
{deleted}theme_listWhat it does lists themes, everyone’s or a
handle’s, the latest first, or withfindthose whose name starts so, as{themes: [{uri, name, creator, palette, createdAt}]}
Resources
Section titled “Resources”The same server offers resource templates (resources/templates/list), so
a client that reads MCP resources opens a pull, an issue, a diff or a file
by URI rather than by a tool call: omp as read mcp://knotless://…, Claude
Code as an @ mention. A URI mirrors the thing’s page on knotless.dev
under the knotless:// scheme, {owner} a handle or DID and {did} and
{rkey} those of the pull’s or issue’s at:// URI, as its page has them.
Each answers with what its tool answers, read as the account the tools act
as, and --read-only serves them all:
| Template | Answers as | Type |
|---|---|---|
knotless://{owner}/{repo}/pulls | pull_list, with no query | application/json |
knotless://{owner}/{repo}/issues | issue_list, with no query | application/json |
knotless://{owner}/{repo}/pull/{did}/{rkey} | pull_show of at://{did}/dev.knotless.pull/{rkey} | application/json |
knotless://{owner}/{repo}/pull/{did}/{rkey}/diff | pull_diff, with no paths: the numbered diff, or, too long to give whole, its said and files as JSON | text/plain |
knotless://{owner}/{repo}/issue/{did}/{rkey} | issue_show of at://{did}/dev.knotless.issue/{rkey} | application/json |
knotless://{owner}/{repo}/file/{rev}/{+path} | repo_read at rev: text as it is, a binary file as a base64 blob (application/octet-stream) | text/plain |
A part is percent-escaped as a template’s expansion escapes it: a branch
topic/x is topic%2Fx in {rev}, a DID’s : may be %3A, and
{+path} keeps its /. resources/list lists none: there are too many
to list. A URI no template makes is a -32002 (resource not found) error
naming the templates. A read that fails is an error with the message its
tool’s would have, and one past the account’s share of a server’s calls
(see Agents) says to wait.
Older than the appview asks for, a read fails as a tool does.
Long files
Section titled “Long files”An agent pays for what it sends and reads as tokens, so the tools let it
change part of a long file without moving all of it: repo_search with
path and context finds the lines and what is around them, and
repo_commit with edits sends only what changes. repo_read with start
and end reads a part when the search shows too little.
The test an_agent_changes_a_line_of_a_long_file
(crates/knotless-mcp/tests/mcp.rs) benches one change both ways, and
fails if editing ever costs more than a fiftieth of rewriting:
| One line of a 138 KB file | Sent | Received | About | Time |
|---|---|---|---|---|
Before: repo_read, then repo_commit with content | 143,115 B | 138,207 B | 70,330 tokens | 473 ms |
Now: repo_search with context, then repo_commit with edits | 313 B | 634 B | 236 tokens | 351 ms |
Tokens are bytes over four. The run behind these numbers is the test’s
own, against the reference PDS on one machine; cargo test -p knotless-mcp --test mcp an_agent_changes_a_line_of_a_long_file -- --nocapture prints
them again. The saving grows with the file: what an edit sends does not.