Skip to content

MCP tool reference

Kangentic ships a built-in MCP (Model Context Protocol) server that gives most agents it spawns a set of tools for working with the board, backlog, sessions, and the embedded browser while they run. It is enabled by default. Which agents get wired, and how, is in How the MCP server works.

When an agent discovers follow-up work - bugs, refactoring opportunities, or new features - it can file tasks directly on the board instead of you entering each one by hand. Agents can also search existing tasks to avoid duplicates, read prior session transcripts, inspect board status, and drive a task’s Browser pane.

The server runs as an in-process streamable HTTP transport inside the Kangentic app. By default it binds to loopback (127.0.0.1) with a fresh token generated on every launch, and Kangentic wires it into each session automatically - there is nothing to install and, for local use, nothing to configure. To turn it off, switch MCP server off in Settings > MCP Server or set mcpServer.enabled to false. To reach the server from another machine, widen the bind interface with mcpServer.bindAddress (a wildcard like 0.0.0.0 still binds loopback) and allowlist a hostname with mcpServer.callbackHost - see the Configuration reference.

This page lists every shipped tool, grouped exactly as the Settings > MCP Server panel groups them, with one anchor per tool (for example #kangentic_create_task) so the app can deep-link a tool’s pill straight to its entry here.

Every tool is annotated read-only or mutating. This is load-bearing for plan mode: Claude Code auto-approves read-only tools without a permission prompt while planning, so the plan-mode auto-approval surface is exactly the read-only set. Mutating tools - the board and backlog writers (create, update, delete, move, promote, link), every workflow tool except the two reads, and the browser tools that change the loaded page or a surface’s lifecycle (navigate, history, click, hover, type, keypress, scroll, drag, select option, drop files, handle dialog, set viewport, eval, open pane, close pane, pop out, dock) - still prompt in plan mode by design. Outside plan mode, Claude Code and Grok Build sessions never see a prompt for Kangentic’s own tools. Every other agent applies its own approval rules to them, as to any MCP server, so it may ask in its terminal before a call (see When agents skip the permission prompt).

Most tools accept an optional project parameter - a project name (case-insensitive) or UUID - to route the call at a different Kangentic project than the one the session is bound to. There are two exceptions. kangentic_get_current_task resolves the agent’s own working directory and branch, so cross-project lookup makes no sense there. The whole kangentic_browser_* family takes no project argument at all: those tools are bound to the project in the connection’s own URL, which makes “there is no path to another project’s pane” a guarantee of the tool shape rather than a runtime check. kangentic_browser_close_pane is the one browser tool that can reach beyond its own project, and only through an explicit includeOtherProjects flag that is off by default.

When project is set, the response is prefixed with [Project: <name> (<shortId>)] so the caller can confirm where the action landed. Use kangentic_list_projects to discover valid selectors. kangentic_move_task_to_project skips the prefix: it resolves two projects at once (source via project, destination via targetProject), so its response names both directly instead.

Operations on individual tasks.

Create Task - add a task to any board column or the backlog. This is the only task-creation tool. With no column it lands in the To Do column; pass column: "Backlog" to stage it in the backlog instead. The Done column is refused - a task created there would be archived off the board the moment it existed, and the error says so rather than reporting the column as missing. Titles are capped at 200 characters and descriptions at 50,000. Creating a task in a column with auto-spawn enabled also starts an agent session for it. As a runaway-loop safeguard, the server caps creation at 500 tasks per app launch; once that cap is hit this tool returns an error until Kangentic is restarted.

Beyond the title and description, a call can carry a priority (0 for none through 4 for urgent), labels, and attachments, a list of absolute file paths with an optional display filename each. The tool’s own description tells an agent to attach any local file the user named by path rather than merely reading it, so a handoff mockup or spec lands on the card. prUrl and prNumber link the task to an existing pull request at creation, which is the only way to do that here: a URL written into the description is not a link.

The spawn behavior can be pinned per task with optional overrides: agentOverride locks the CLI for the task’s lifetime, modelOverride and effortOverride set the model and reasoning level, and permissionMode sets the permission mode (default, plan, acceptEdits, dontAsk, bypassPermissions, or auto). Each falls back through the column override, then the project default, then the app default when omitted. Passing any of the four implies runMode: "agent_override"; pass that on its own to pin whatever the task would resolve to today. profile rides a Board profile instead and is mutually exclusive with the pins. autoCommand delivers a message to the agent once it spawns. It is MCP-only, and it is sent in place of the destination column’s first Send message to agent row for this task; on a column with no such row it goes out once, after the column’s On task enter automations. It is not sent when the task’s main session starts over from a recap of a deleted conversation (see Session persistence), or on a move out of Done.

One guardrail runs before anything is written. When the call carries no project and the title or description names a different registered project, the tool refuses with a routing check and creates nothing: the message names the mentioned project and the two re-runs that resolve it, project: "<mentioned>" to file it there or project: "<active>" to confirm. An explicit project, even one naming the active project, is honored as a deliberate choice.

Since v0.37.0 agentOverride, modelOverride, and effortOverride are validated when the task is created, not when it spawns. An unknown value is rejected on the spot with the valid values listed, and no task is created - a typo used to produce a card that looked correct and failed hours later, when someone moved it into an executing column. The rejection names the resolved agent and which rung of the ladder it came from, since the caller usually did not pass one. Validation stays permissive wherever it cannot know: an agent that enumerates nothing accepts the value and leaves its own CLI as the judge, a floating alias like opus is always accepted (discovered model lists hold only concrete versioned ids), and a capability probe that runs out of time accepts too. Only the fields present on the call are checked. A friendly name with a space is converted to the CLI’s spelling ("Opus 4.8" to claude-opus-4-8). Since v0.44.0 a single word such as "Opus" is stored as written, and the Claude adapter turns it into the floating alias opus at spawn when Claude’s /model picker lists that family; it used to become claude-opus, which passed here and failed at spawn.

useWorktree: false means no worktree is created: the agent runs in the project directory itself, and the task detail’s folder button opens that folder rather than a worktree. What happens to the branch depends on what else you passed. A branchName is fetched, created if it does not exist, and checked out in the project directory, so it is still subject to the conflict guard below. With no custom branch but a baseBranch, that base is checked out and fast-forwarded from origin. With neither, git is not touched at all and the agent runs on whatever branch the repo currently has out. Either checkout refuses on a dirty working tree, so commit or stash first. Omit useWorktree to get a worktree whenever the project allows them; while the project has Worktrees off in Settings > Git, true is ignored and the task runs in the project folder.

Labels are worth one call first: kangentic_board_summary reports the board’s existing label vocabulary, so an agent can reuse a label instead of coining a near-duplicate. There is also a client-side quirk to know about. When a call carries both labels and a description of roughly 1KB or more, the labels can be dropped before they ever reach Kangentic, upstream in the MCP client’s own tool-call emission. Kangentic detects that signature and appends a [Labels not received] line to the response naming the follow-up call to make. It is advisory rather than an error - the task itself was created correctly - and a caller that deliberately sent no labels never sees the line.

Naming a branch with branchName is checked before anything is written. Git allows a branch in only one working tree at a time, so if that branch is already checked out the call is refused, naming the branch and the path holding it, and no task is created. Retrying the same call fails identically; free the branch or use a different name. See Naming a branch yourself.

List Tasks - browse tasks in board order (top to bottom within each column), optionally filtered by column. Each task reports position, its zero-based ordinal slot within its own column. That is the same slot kangentic_move_task and kangentic_reorder_tasks accept, so a listing can be read and handed straight back. Each task also renders its labels as [a, b] after the title, omitted entirely when it has none. Filtering by the Done column resolves but returns nothing, since a task moved there is archived off the board; reach completed tasks with kangentic_search_tasks instead.

Search Tasks - keyword search across both the board (active and archived) and the backlog. Use the scope parameter to narrow to a single surface; results are grouped so you can see which surface each hit came from. Board and backlog hits both render their labels the same way, but note the asymmetry in matching: backlog items match on their labels, board tasks match on title and description only, so a board task is not found by searching for one of its labels.

A query of # followed by digits (#42) is a ticket lookup by the task’s #N number, prefix-matched. Backlog items have no ticket number, so a ticket query returns board tasks only whatever scope says.

Find Task - look up a task or backlog item by display ID, UUID, branch name, title keyword, or PR number. Returns matching board tasks (with branch, worktree path, and PR info) and any matching backlog items.

Since v0.44.0 each of the first five matched board tasks also carries what the Knowledge Graph knows about it, so “what did #412 ship?” is one call:

  • Summary. For a finished task, the sentence or two an agent wrote about it, with the date (see Task summaries). A finished task without one says it is not written yet, or that Task summaries are switched off.
  • Commits. The default-branch commits linked to the task, newest first, at most three with the total.
  • Changed files. The files its sessions changed, most-changed first, at most eight with the total.

Past five matches, one line says to look a task up by displayId for these details. With the index off, one line says so and nothing is read.

Current Task - resolve the task for the current working directory or git branch. Use it at the start of work in a worktree to confirm which task you are on before commits, PRs, or merge-back. This is the one tool that does not take a project argument. A single match prints the same task line and Knowledge Graph lines kangentic_find_task does (branch, base, worktree, PR, summary, commits, changed files), and an ambiguous match lists every candidate.

It deliberately does not report the task’s reserved dev-server ports. This serializer is a per-project read while the port ledger is global, so including them would make every task lookup hit a second database once per matched task. Use kangentic_check_dev_ports, which answers that question and probes each port besides.

Task Stats - token usage, cost, agent runtime, tool calls, and lines changed for a specific task or aggregated across all tasks, with an optional sort metric. Across all tasks, each row starts with the task’s #N, so the agent can follow one up.

Cost and agent runtime are the completed task view’s figures. A resume is a new session record that reports its conversation’s running total, so before v0.46.0 a task resumed ten times counted that conversation ten times. Each conversation is now read once and the conversations are added, which is also how kangentic_board_summary totals the board. For one task, the runtime line reads Agent runtime.

Since v0.42.0, asking for one task by taskId also returns its subagent fan-out: one row per subagent type with token counts, turn count, and how many distinct subagents ran, plus a Fan-outs section grouping the same turns by the driver turn that started each one, heaviest first. On a /code-review or /test task that is usually most of the traffic, and it is the only place the board can say which subagent was expensive. The rows carry no cost of their own, because the task’s reported cost already covers the whole session tree. Only Claude Code reports subagent usage today; the aggregate call (no taskId) carries no breakdown.

Update Task - edit a task’s title, description, PR info, agent assignment, model/effort/permission overrides, run mode, board profile, priority, labels, base branch, worktree placement (useWorktree), and attachments. The model, effort, and permissionMode overrides mirror the ones on kangentic_create_task; pass an empty string to clear one and fall back to the column, project, or app default. agent, model, and effort get the same call-time validation as their create counterparts, resolved against the agent this task actually runs under. A task’s stored pins are never re-validated, so a labels-only follow-up still succeeds on a task carrying a since-deprecated model, and labels carries the same large-description caveat and the same [Labels not received] advisory.

A write that changes model, effort, profile, or runMode on a task with a running session reaches that session the way a context bar pick does. When the session runs at something other than the new settings, it is restarted with them as launch flags, in the background after the call returns. A session already on the new settings is left alone, and so is one whose stored settings the write did not change. The restart ends the session’s turn in progress. When an agent updates its own task, that is the agent’s own turn, and the reply says so.

profile and runMode choose how the task gets its agent settings, and they are mutually exclusive. A profile (a Board profile name or id) re-resolves per column as the task moves; pass an empty string to clear it back to Default. runMode: "column_settings" follows each column and clears the pins, while runMode: "agent_override" pins agent, model, effort, and permission for the task’s whole life. Setting any pin already implies agent_override, so you only need runMode to switch modes without pinning anything, and pairing a pin with column_settings is rejected as a contradiction. Attachments passed here are added to the task; existing ones are kept (remove one with kangentic_remove_task_attachment). To move a task between columns, use kangentic_move_task instead.

The description can be changed three ways, so an agent editing a long description does not have to resend the whole thing:

  • description replaces the entire description.
  • descriptionEdits applies an ordered list of exact find-and-replace edits to the current text, like the file Edit tool. Each find must appear exactly once (as the text stands after the previous edits in the list); a missing or non-unique find fails the whole call and writes nothing.
  • appendDescription adds text to the end of the current description.

descriptionEdits and appendDescription can be combined (edits apply first, then the append), but neither can be used together with a full description. Descriptions are capped at 50,000 characters.

Move Task - move a task between columns, optionally placing it at a chosen slot in that column, running the same lifecycle as a drag: spawning or suspending agents, creating or cleaning up worktrees, and running the source column’s On task exit and the destination’s On task enter automations. Moving to Done auto-archives the task; moving back to To Do kills the session, removes the worktree, and drops the PR link of a task that did its own work.

The optional position is a zero-based ordinal slot among the column’s tasks; the tasks at and below it shift down. It is clamped to the column, so a value past the end lands last, and omitting it appends. Naming the task’s current column together with position repositions it in place, which changes nothing but its order: no session, worktree, or lifecycle effects. Position has no useful effect when moving into Done, which archives the task.

Since v0.46.0 two parameters make a move a hand-off:

  • column: "next" moves the task to the column after its current one. For a workflow task, that is the next column on its route. A column actually named “next” still wins.
  • message (up to 20,000 characters, line breaks kept) reaches the agent in the destination column once it is ready, after that column’s own On task enter automations. The move and the message are one call because a separate kangentic_send_session_message cannot reach a session the move itself is suspending or starting. A message on a move that stays in the task’s column is refused, since no agent arrives to read it.

A task in a running workflow is locked. Only the workflow, its column automations, and the task’s own agent may move it, and the task’s own agent moving its card is how a column’s work reports itself done. Any other agent is refused with “#414 is locked while Harden the API runs.” While the workflow is paused, any other agent is refused until it resumes. No agent can move a workflow task into To Do; kangentic_remove_from_workflow comes first.

Reorder Tasks - set the top-to-bottom order of tasks within one column in a single call, for sequencing a column by priority or execution order. Pass column (case-insensitive) and taskIds, an array of display IDs or UUIDs in the order they should appear from the top; every ID must already be in that column.

The listed tasks take the top slots in the order given, and any task in the column you do not list keeps its relative order below them. So you can pass every task to set the full order, or just a few to pin them to the top. Read the current order first with kangentic_list_tasks or kangentic_get_column_detail. This never moves a task between columns and never spawns, suspends, or otherwise touches a session or worktree; use kangentic_move_task to change a task’s column.

Move Task to Project - relocate a task from the To Do column of one project’s board to a different project’s board. Only To Do tasks can move, since entering To Do already resets a task’s live state (session killed, worktree removed, branch deleted), so nothing live has to cross the project boundary; move a task to To Do first if it is elsewhere. The relocation preserves title, description, labels, priority, creation time, and attachments, and assigns a new task ID and display number in the target project. Pass targetProject (the destination, which must differ from the source) and optionally column to land somewhere other than the destination’s To Do column - landing in an auto-spawn column there spawns an agent, just like kangentic_create_task, and the destination’s Done column is refused the same way. The project parameter names the source project (omit to use the active one), so this is the one tool where project is not the destination; its response states both the source and destination directly instead of the usual [Project: ...] prefix. A task in a workflow is refused, because the relocation makes a new task and would orphan its links; take it out with kangentic_remove_from_workflow first. A workflow already places each task on whichever board its work belongs to.

Link PR - authoritatively resolve and attach a task’s pull request via the hosting provider’s CLI (gh for GitHub, az for Azure DevOps; which one answers is decided by the repository’s git remote, see Hosting providers), even for PRs opened outside Kangentic and even when the task has no live session. Re-running refreshes the linked PR’s state and merge readiness. When the provider’s CLI cannot check, the error names the actual reason and the task keeps whatever link it has.

Since v0.41.0 the optional branch parameter names the remote branch the work was pushed to, recorded on the task before resolving. Pass it for a task with no worktree whose push Kangentic did not capture - such a task otherwise has nothing to search by, and the tool tells the two cases apart: “searched and found nothing” is a normal result, while “nothing to search by” is a refusal naming the remedies (pass branch, or set prUrl / prNumber with kangentic_update_task).

Delete Task - permanently remove a task, its attachments, and session records, cleaning up the associated worktree and branch, including any processes the session left running inside the worktree. This cannot be undone.

Remove Attachment - remove a single attachment by its attachment ID, from either a board task or a backlog item (the ID alone determines which surface owns it). Find attachment IDs with kangentic_query_db. This cannot be undone.

Columns, the backlog, projects, and unified search.

List Columns - see every board column in board order, with names, roles, and task counts. Since v0.41.0 the Done column is included: it reports its completed count rather than a live count, since a task moved there is archived off the board. Read the roles rather than assuming the last column is the finish line - on the default board the last non-Done column is Merge. A column you archived deliberately stays hidden; the carve-out is the Done role alone. A column that runs tasks on its own conversation is tagged [isolated session]; a main-session column is the norm and carries no tag.

Column Detail - inspect a column’s settings: description, auto-spawn, permission mode, session track, plan-exit target, and visual settings. Session, On task enter, and Handoff context always print, even at their defaults, because a session track is never absent and hiding the default would leave a caller unable to tell a main-session column from a failed write; the overrides print only when set. It also returns taskOrder, the column’s tasks top to bottom with their zero-based ordinal position, which makes this a complete read-before-write call for kangentic_reorder_tasks. On the Done column, taskOrder is empty and a Completed count reports the size of the archive instead. The column’s automations print in their three groups (On task enter, On agent turn end, On task exit), each row with its Condition, Text match, or Frequency limit when it has one. For the full rows, read kangentic_list_automations.

Update Column - rename, recolor, and configure a column: its description, auto-spawn, the message delivered on entry, agent/model/effort overrides, permission mode, handoff context, session track, and the plan-exit target column. The role columns (To Do, Done) are editable like any other; their role itself is structural and not a settable field.

The message parameter is autoCommand, kept for the common case. It accepts plain prose as readily as a slash command, and it writes the column’s first Send message to agent automation in its On task enter group, creating one if the column has none; null deletes it. autoCommandMode (immediate or deferred) sets that row’s delivery. A row with a Condition or a Frequency limit is delivered only when both pass. For anything past one message (a second row, a script, a webhook, a notification, a Move to column row, or a row on another trigger) use kangentic_set_automations.

sessionTarget (main or isolated) picks which session a task runs on in the column, and sessionSpawnStrategy (create_or_resume or always_spawn_new) what the column does with it on entry. Neither is nullable; pass the default to go back. Changing sessionTarget carries sessionSpawnStrategy with it unless you pass one explicitly: switching to isolated also switches to always_spawn_new, and switching back to main returns it to create_or_resume, so an isolated column runs an independent pass per entry by default. A strategy you set deliberately is never overwritten. For a persistent isolated track that resumes across entries, pass sessionTarget: "isolated" and sessionSpawnStrategy: "create_or_resume" together. This is the same rule the Column Manager applies.

Create Column - add a new column to the board, with optional color, icon, and the same settings kangentic_update_column takes: autoCommand (which creates the column’s first Send message to agent automation), autoCommandMode, sessionTarget, sessionSpawnStrategy, the overrides, and a position. With no position the column lands just before Done, where a new stage almost always belongs; a position is a zero-based slot clamped between the role columns, so nothing can land before To Do or after Done. A column that checks an earlier column’s work wants sessionTarget: "isolated", since on the default main track the reviewer shares the task’s conversation and is the same agent that wrote the code. A working review column is one call: a name, autoCommand: "/code-review", and sessionTarget: "isolated".

Delete Column - remove a column. Refuses non-empty columns and the role columns (To Do and Done). Naming Done still gets you the role refusal rather than a confusing “not found”, even though Done is stored archived. Deletion transactionally cleans up the column’s automations, other columns’ plan-exit targets, and Board profile references.

List Automations - read a column’s automations, or the whole board’s by omitting column. This is the right first call for “what does this board do”: a board-wide read answers it once instead of one call per column. Rows print in three groups, On task enter, On agent turn end, and On task exit. Each row reports its id, name, type, on (enter, turn_end, or exit), position, enabled, condition, finalLine, maxRuns, and the type’s own fields; the three guards are null when the row has none.

The text gives each row one line: its name, its type, the sentence the Column Manager shows, then every option the panel marks, in words, in parentheses. A Move to column row with a message, a Condition, and a Frequency limit reads Moves the task to Executing (with a message; only if the review found work the implementer can do; at most 3 times for each task), and a Text match reads only if the last line reads "Verdict: Ready".

A row that cannot run as things stand is flagged with the reason: its switch is off, it is a send_message on a column whose auto-spawn is off, or it is an On task enter row on To Do or Done, where nothing runs on entry. An On agent turn end row is also flagged on To Do or Done, on a column whose auto-spawn is off, and on a column whose agent cannot report when it finishes a turn (“This column’s agent can’t report when it finishes a turn.”).

Set Automations - replace one column’s automations wholesale. The automations array (max 50) becomes that column’s entire list, in order, so read the current rows with kangentic_list_automations first and send them back with your change applied; pass [] to remove them all. Each entry takes name (required, unique within the column ignoring case), type (send_message, run_script, webhook, notify, or move_to_column), an optional on (enter by default, or turn_end or exit), an optional enabled (true by default), an optional id, the optional guards below, and the type’s own fields flat on the object. Carry each existing row’s id through so it keeps its identity; a row with no id is created fresh. Two of the flat fields differ from their kangentic.json shape: a webhook’s headers is one string here, one header per line as Name: Value, not an object, and a notify row takes only title (its body keeps the default, {{toColumn}}).

A move_to_column row runs only on turn_end. It takes targetColumn (omit it for the next column, which for a workflow task is the next one on its route) and an optional message for the agent where the task lands. A row that moves the task ends the group, so the rows below it do not run. On turn_end, every text field also takes {{agentReply}}, the agent’s final message of the turn, which is how a review’s findings ride the move back to the column that fixes them.

Every type takes the same three guards, all off by default:

  • condition: a sentence in plain words that the Automation agent (Settings > Workflows) answers yes or no before the row runs. A no is not a run, so the next trigger checks it again. Until an Automation agent is set up, a row with a condition is skipped.
  • finalLine: a Text match, the other kind of Condition, checked in code with no agent call and no cost. The row runs only when the last line of the agent’s final message matches the pattern, ignoring case, punctuation (markdown marks included), and extra spaces. * is a wildcard and the pattern has to cover the whole line, so Verdict: Ready is exact and PR: * starts with. turn_end rows only. A miss is not a run.
  • maxRuns: Frequency, the most times the row may run on one task across every visit to the column. The count starts over when the task leaves Done.

A row holds one kind of Condition, so a condition and a finalLine together are refused (“give a condition or a finalLine, not both”). See Automations for how the guards and the turn-end group run.

Whole-column rather than per-row because a reorder, a delete, and an insert arrive together, and applying them separately would make the unique-name rule reject an intermediate state the final state does not have. A row the column cannot run as things stand is stored and warned about, not refused: building a list before switching the column’s agent on is a legitimate order of operations. What cannot be what the caller meant is refused, with the reason, and every such refusal ends “Nothing was saved.”: an unknown on (a misspelled turn_end used to become an enter row), a type on a trigger it does not run on (move_to_column on enter or exit), a maxRuns that is not a whole number of at least 1, a finalLine on an enter or exit row, and a finalLine with no letter, number, or *, which no line can match. send_command is accepted as an alias for send_message (and command for message) for skills written before the rename.

Automation Runs - read the run log, newest first, by task (everything that ran for one card) or by column (every run of the automations that column holds now), with a limit of 1 to 50. Each run carries its status (running, succeeded, failed, skipped, or interrupted), a one-line detail (the failure reason, the skip reason, or a success such as HTTP 204 or exit 0), the attempt count, and its timestamps. The log keeps the automation’s name and type on the row, so renaming or deleting one does not empty its history. A row left running was in flight when Kangentic quit and is marked interrupted the next time the project opens.

A run whose row has a Condition also says whether the Automation agent found it met, with the agent’s one-line reason, and a Text match run says [text match met: ...] or [text match not met: ...] with the line it read. A run that a check answered gets one more line naming that call: the agent and model, the effort, the four token counts and cost as the CLI reported them, how long it took, and how many rows the one call answered. An agent that reports no usage says so instead of showing zeros. A Text match run has no such line, since no agent was asked.

checked by Claude Code (claude-haiku-5-5, low): 2,104 fresh input + 45 output, 120 cache write, 1,012 cache read, $0.0026, 1.9s, one call for 2 rows

Run Automation - run one automation now, named by column and automation, against the named task’s current state. Use it to retry a failed automation or to test one you just wrote. Current state, not the state of the move that first ran it: {{toColumn}} resolves against where the task is now, while {{fromColumn}} and {{trigger}} resolve empty because there is no move. It writes a fresh run row either way. An On agent turn end row whose Text match, Condition, or {{agentReply}} reads the final message is judged against the newest one the task’s session wrote; a message from a session a move has since replaced counts as none. It will not start an agent: a send_message against a task with no live session is recorded as skipped rather than spawning one. It takes the task lock and is capped at five minutes, so a hung row cannot wedge that task’s next move.

List Board Profiles - read the board’s named per-column strategy ladders: id, name, description, and the per-column settings each one overrides. Returns JSON, because the primary uses are diffing two boards and copying a profile between them, and both need the exact structure back out. Entries for columns that no longer exist are omitted, so the listing reflects what will actually apply. Profile entries are keyed by column name, which is what makes copying a profile between projects work. See Board profiles.

Create Board Profile - add a named preset of per-column agent, model, effort, permission, auto-spawn, handoff, session-track, and plan-exit settings. Names must be unique on the board. The columns object is sparse and keyed by column name, so list only the columns this profile changes. A column’s message to its agent is not a profile field: automations are shared by every profile, and the retired autoCommand and autoCommandMode keys are refused with a pointer to kangentic_set_automations rather than accepted and ignored. kangentic_create_task and kangentic_update_task accept a profile parameter so an agent can file tasks that ride a specific ladder.

Update Board Profile - rename a profile, change its description, or retune its per-column settings. columns merges by default, so retuning one column does not wipe the rest, which is what makes a sweep like “change every profile’s Opus 4.8 to Opus 5” safe to run column by column. Pass replaceColumns: true for a wholesale swap, the usual choice when copying a profile from another board. On the active project this reaches live sessions exactly as saving the profile in the Column Manager does.

Delete Board Profile - remove a profile. Tasks riding it are not rewritten; they fall back to each column’s own settings, and the response reports how many were affected. That fallback is itself a settings change for a running session, so on the active project it propagates like any other profile edit.

Board Summary - a high-level overview: task counts per column (the Done column included, reporting its completed count), the board’s label vocabulary, active sessions, completed tasks, and aggregate cost and token metrics.

The label vocabulary answers “what labels does this board already use”, which a paginated, count-free task listing cannot. It reports the labels in use with their counts, most-used first, tallied across active tasks, archived (Done) tasks, and backlog items - archived included because on a mature board most of the vocabulary lives in Done. The header carries two figures that measure different things: N distinct is the size of the vocabulary, while M labelled items counts items carrying at least one label, so the per-label counts (which are label uses) sum to more than M. The printed list is capped at 30 with an “and N more” tail, while the structured data.labels carries the whole tally uncapped. Label colors are deliberately not reported: passing an existing label back as a plain string preserves the color it already has.

Usage Stats - tokens, cost, burn rate, active time, and by-model / by-agent / by-effort / by-subagent-type usage for a project or all projects over a time range. The MCP counterpart of the Usage Stats.

Tokens come back as four disjoint types (fresh input, output, cache write, cache read) with no combined total, and earliestTurnMs says where that coverage begins, which on an older install is later than the cost ledger’s own start. activeMs travels with activeSessionsCovered because the interval ledger behind it reaches fewer sessions than sessionCount does. Cost is API-equivalent list price and is stored per leg, so a conversation resumed twenty times is counted once. See What the numbers count.

The subagent breakdown (bySubagentType and the subagent* totals) covers the Task-tool subagents a session fans out: the finders a /code-review spawns, the agents a /test runs. It is additive to the turn token fields and to both time series, which stay main-thread so the historical series remains comparable, and it carries no cost of its own since totalCostUsd already covers the whole session tree. subagentNestedCount names how many of those subagents another subagent spawned; it is a subset of the count, never an addend. byAgent is a different axis: the CLI that ran the session, not a subagent inside one. A Not counted: line names agents in the range that cannot report subagent usage at all; only Claude Code can today, so a Codex or Gemini range says so rather than reporting an empty breakdown that reads as “nothing fanned out”. For one task’s fan-out, use kangentic_get_task_stats with a taskId.

Since v0.46.0 a Background (not in the totals above) line reports the calls Kangentic made on your behalf in the range: Condition checks, workflow step checks, task summaries, task titles, and Command Terminal names. It gives the call count, cost, the four token types, and the calls per job, the same numbers as the dashboard’s Background card. None of it is added to a session figure. data.background carries the totals and the per-job figures; its per-day rows are a series, so they come back only with includeSeries.

List Backlog - see items staged in the backlog, optionally filtered by priority or keyword.

Promote Backlog - move backlog items onto the board as tasks (defaults to the To Do column; the Done column is refused, as on kangentic_create_task). Attachments are copied to the new tasks.

Update Backlog Item - edit a backlog item’s title, description, priority, labels, or attachments. Labels are replaced wholesale, not merged; attachments passed here are added to the item, keeping existing ones (remove one with kangentic_remove_task_attachment).

Delete Backlog Item - permanently remove a backlog item and all of its attachments. This cannot be undone.

List Projects - every Kangentic project registered on this machine, with name, id, path, and an active flag. Use a returned name or id as the project argument on any other tool.

Search - unified search across board tasks, backlog items, session events, project names, your agents’ past conversations, and the commits on the default branch in one call. This is the same backend as Quick Find. Scopes to the active project by default; pass scope: "all" to widen across every registered project.

Since v0.44.0 conversations are matched by meaning and keyword by default (mode: "hybrid", fusing keyword and local semantic search), and mode: "keyword" matches literal words only; mode only affects the conversation results. On an Intel Mac, where the local model cannot run, hybrid answers by keyword. Pass taskId to restrict conversation and commit hits to a single task’s history. Each conversation hit carries a sessionId and turnUuid - follow up with kangentic_get_transcript (aroundUuid) to read the surrounding turns. Conversation results require Conversation memory indexing to be on (the default), and rank semantically only when the Knowledge Graph switch in Settings > Knowledge Graph is on; otherwise the response says conversations were matched by keyword only. This tool subsumes the former kangentic_recall.

A ## Commits section lists the default branch’s commits the query matches, by keyword on the subject and body, and for a query of 7 to 40 hex digits by sha prefix as well. Each row names the task the commit is linked to (from #529 ...) or says none is. The link is where the subject was first written, not a merge record. Commits need the index on.

Two parameters rank tasks instead of listing hits. Both work on one project at a time, so neither takes scope: "all":

  • groupBy: "task" rolls every matching conversation passage, task record (title, labels, description, summary), and commit up per task, and returns the tasks strongest first with their facts on one line (cost, duration, tokens, conversations, files, lines, outcome, pull request). The strongest twelve also carry the task’s summary, its best passage, and a sessionId and turnUuid to open. It answers “which tasks touched X?” and “how many times did we change Y?”, which a capped list of snippets cannot count. It is the same rollup the Knowledge Graph’s Ask runs before its agent starts.
  • relatedToTask ("#N", "N", or the task id) ranks the tasks most like one task, up to twelve rows in the same shape: “has anything like this been done before?”. It is the opposite of taskId, which searches inside one task. query is optional with it and focuses the ranking.

A query of # followed by digits (#42) is a ticket lookup: it matches tasks by their #N number and returns only task hits, skipping backlog, projects, events, conversations, and commits. The match is a prefix (#4 finds 4, 40, 41), with the exact number first and active tasks ahead of archived ones. kangentic_search_tasks accepts the same syntax on the board scope. A bare number with no # stays an ordinary text search.

A workflow is a set of linked tasks that run through their boards on their own. Each task has a route, the columns it visits, and starts when every task it waits for has reached Done. These tools plan a workflow, start it, and change it while it runs. Every handler checks what it is given and refuses with what to fix, writing nothing.

A workflow lives in the project it was made in, so project reaches another project’s workflows. Its tasks can sit on any registered project’s board. A planned task names its board, and the tools that take a task also take taskProject (or blockerProject and blockedProject) for a task that is not on the workflow’s own board, since task numbers count per board.

The workflow’s own agent, the one that plans it in the Workflows view, connects to a narrower server. It gets the read-only tools and the seven planning tools: save, add task, link, unlink, set route, remove from workflow, and request project access. Its planning tools drop workflow and project and act on its own workflow. Start, pause, resume, delete, the browser tools, and every tool that creates, moves, or deletes board work are not registered for it, whatever its CLI would otherwise allow. Start is how a person approves the plan.

List Workflows - this project’s workflows, one line each, with the state (not started, running, or paused), progress, and revision. A workflow whose tasks are all in Done is left out unless includeFinished is set.

Get Workflow - one workflow: its state, its revision, and each task with its column, its route, and what it waits for. A task or a blocker on another project’s board says which board. Before Start, each planned task also lists any setting that is not the default (project, base branch, worktree, profile, or agent override) in kangentic_save_workflow’s own names, so a plan read and sent back keeps them. The workflow’s own agent can leave workflow out to read its own.

Save Workflow - create a workflow, or replace the plan of one that has not started: its title, every planned task, and the links between them. Omit workflow to create one, with a title and optionally the brief it was made from. Nothing reaches a board until it starts.

A save replaces the whole plan, the way kangentic_set_automations replaces a column’s rows. Read the plan with kangentic_get_workflow first and send back the revision you read. A save over a newer revision is refused, so a person’s edits in the Workflows view win.

tasks holds at most 100 planned tasks. Each takes a key unique in the plan, a title, and a description, which is the brief its agent reads. It can also take:

  • project to put it on another registered project’s board, or existingTask to take in a task already in that board’s To Do instead of creating one
  • routeStart, routeLast, and skip, column names that shape its route; left out, the route is every column between To Do and Done
  • labels and priority
  • the run settings a task on the board has: baseBranch, useWorktree, runMode, profile, agentOverride, modelOverride, effortOverride, and permissionMode, with the meanings and the checks they have on kangentic_create_task. Start copies them onto the task it creates.

links pairs keys as { blocker, blocked }: the blocked task waits for the blocker to reach Done. The whole plan is checked before anything is written, and a refused save of a new workflow leaves no empty one behind. A refusal names the problem: a duplicate key, a column a route cannot use, a link to an unknown key, or a link that closes a loop. Once the workflow has started its plan is its tasks, and a save is refused with the four tools that change a started workflow.

Start Workflow - start a planned workflow. Its tasks appear in To Do on their boards, and each starts on its own once every task it waits for is in Done, as agent slots allow. The task the workflow was made from moves to Done, if it is still in To Do and the plan did not take it in. Start checks the plan again first, so a board removed since the save, or a taken-in task that has left To Do, refuses the start and names the task to save again. The tool’s description tells an agent to start a workflow only when you ask.

Pause Workflow - pause every agent in a running workflow at once. Nothing starts or moves until it resumes. While it is paused, you can still move its cards by hand, except into To Do.

Resume Workflow - resume a paused workflow. Each task’s agent resumes in the column it is in, and waiting tasks start again as their blockers finish. Resume also retries a task start that keeps failing.

Add Workflow Task - add a task to a started workflow. It lands in To Do and starts once nothing it waits for is left and an agent slot is free. It takes a title, a description, taskProject for another board, routeStart, routeLast, skip, and waitsFor: the tasks in this workflow it waits for, each a number on the workflow’s own board or the id of a task on any of its boards. It also takes the run settings a planned task takes, checked the same way.

Link Workflow Tasks - make one task in a started workflow wait for another to reach Done. Only a task still in To Do can wait, and only for one not yet done. A task cannot wait for itself, and a link that would close a loop is refused.

Unlink Workflow Tasks - remove a link in a started workflow; it takes the same parameters as kangentic_link_workflow_tasks. A task in To Do whose last blocker this was starts as soon as an agent slot is free. Removing a link that is not there changes nothing, and the reply says so.

Set Task Route - change which columns a task in a started workflow visits. A task in To Do can take any route. One that has started can only change the columns after the one it is in, and its route cannot end before that column. Pass only what changes: routeStart or routeLast (null resets it to the open end) and skip, the complete list of columns to skip, where an empty list skips none.

Remove from Workflow - take a task out of its workflow. It stays where it is as an ordinary task, and its links are removed. A task in Done stays with its workflow. Removing it first is how a workflow task moves to To Do or to another project.

Request Project Access - let a workflow’s own agent read another registered project’s code while it plans. The tool’s description tells the agent to call it instead of searching the disk. The grant is immediate, needs no approval, and is kept on the workflow, so every later start and resume of the agent reads that checkout too. The agent already reads every project its workflow’s tasks are on.

The reply says when the checkout becomes readable. An idle agent is restarted at once on the same conversation with the checkout added, a busy one when its turn ends, and an agent that is not running at its next start. Only an agent whose CLI can widen what it reads gets the restart, which today means Claude Code; any other agent keeps reading its own project, and the reply says so.

Delete Workflow - delete a workflow. One not started loses its plan and nothing else changes. A started one’s unfinished tasks stay where they are as ordinary tasks, and its tasks in Done stay in Done. The workflow agent’s conversation goes with it. The tool’s description tells an agent to delete a workflow only when you ask.

Per-task session history, transcripts, and handoff.

List Sessions - session records for a task with start and end times, exit codes, suspension reasons, cost, token counts, and runtime. Each record’s cost and runtime are its own share of its conversation’s running total, so the records add up to the task’s total in kangentic_get_task_stats.

A resume is a new record that reports its conversation’s running total, so since v0.46.0 each record’s cost and runtime are its own share, and the records add up to kangentic_get_task_stats. A record that reported no usage has no share. The structured data keeps each record’s running reading beside its share, as cumulativeCost and cumulativeDurationMs.

Session History - read the agent’s native session history file (Claude JSONL, Codex rollout JSONL, or Gemini chat JSON) for a task’s most recent session. A history file over 200 KB is read from its most recent 200 KB only, and the result opens with a notice such as [Truncated - showing last 195KB of 840KB], so a conversation never just starts partway through with no explanation. The 16 MB parse bound belongs to kangentic_get_transcript, which reads the structured transcript instead.

Session Files - absolute paths to every per-session file (activity log, status, settings, MCP queue, native history, and more), each with an exists flag.

Session Events - parsed activity events from a session’s log (the PreToolUse / PostToolUse / Stop / Notification stream), filterable by tail count, timestamp, and event type.

Activity Intervals - the durable history of active versus idle spans for a task or session, from Kangentic’s recorded activity-interval ledger. Shows how long a session spent working versus waiting over time, including the current idle-wait duration for a still-running session. It is the same ledger behind the Usage Stats Avg Active tile, which is why that tile covers fewer sessions than the Sessions count beside it: per-interval recording shipped later than the usage ledger.

Reserve Dev Ports - claim free TCP ports for a task before it starts a dev server, so two agents working at the same time never bind the same one.

Kangentic does not decide what a project’s ports should be. The project already does, in angular.json, a vite config, or a compose file, so nothing is reserved until an agent asks. What Kangentic can do that a project cannot is see every task and every project in the app at once. The case this exists for: several tasks sit in one column and run concurrently, and they all default to the same port.

Ask for every port you are about to bind in one call. A project needing an API and a frontend asks for count: 2, because asking twice leaves a window where a sibling task takes the second. Each returned port has been probed as genuinely free, not merely unclaimed. Getting back fewer ports than requested means the scan range ran out; fall back to the project’s own configured ports for the rest. count is capped at 10 per call.

A reservation lives until its task or its project is deleted, so a restart hands the same task the same ports. The scan range defaults to 7300-7499, picked to sit clear of every common framework default (3000, 4200, 4321, 5000, 5173, 8000, 8080). Change it with devServer.portRangeStart and devServer.portRangeEnd in the global config.json; there is no Settings UI for it. See the Configuration reference.

Check Dev Ports - report what a task holds and what the machine actually says about those ports. Reserves nothing.

Every port reported is probed, because a reservation is a promise rather than a fact. Reading the ledger alone is silent about the case that bites most often: a dev server started outside Kangentic entirely, on a port the ledger never handed out. Pass ports to ask about specific numbers, such as the ones a project’s own config pins, which is the only way to learn that short of trying to bind and failing. At most 20 ports per call, since each one is probed.

Each port comes back as one of four answers: reserved by this task, reserved by another task, in use by something outside Kangentic, or free. Use it to recover ports granted earlier in a session (after a resume, or before restarting a server) instead of reserving again.

Handoff Context - the most recent cross-agent handoff record for a task: which agent handed off to which, when, and the path to the prior agent’s session history.

Get Transcript - read what the agent on another task or project said, as clean structured markdown (default) or verbatim raw scrollback. Structured output supports view, tail, and search levers and a configurable character budget. Pass aroundUuid (a turnUuid from a kangentic_search conversation hit) with an optional context count to pull just the neighborhood of a cited turn instead of the whole transcript.

Pass workflow (an id or exact title) instead of taskId or sessionId to read a workflow’s own agent, the one that planned it. That conversation is structured only, since no raw scrollback is kept for it.

When an agent has deleted its own transcript file (Claude Code removes conversations older than its cleanupPeriodDays), a structured read falls back to the copy Kangentic archived, then to the text Conversation memory indexed, and the header names which: Source: Kangentic's saved copy, or Source: indexed text only with tool calls flattened. A session that started over from a recap is pointed here for its old conversation.

Send Session Message - steer another task’s running agent by sending a message into its session. Self-sends are refused, and a per-target sliding-window circuit breaker stops a runaway sender from flooding a session.

Sent Session Messages - the durable audit log of cross-session messages: what was sent to whom, and whether each landed (delivered, queued, refused, or failed).

The kangentic_browser_* family drives the embedded Browser pane of a task (an Electron webview showing the user’s own dev server) via Chrome DevTools Protocol. Target a surface with sessionId or taskId, or omit both to use your own task’s surface. A surface in another project is refused.

The whole family is gated by the Agent Browser settings. browserAutomation.enabled is the master switch; when it is off the tools are not registered at all. Sub-gates then apply: allowInteraction gates every tool that acts on the page (click, hover, type, keypress, scroll, drag, select option, drop files, handle dialog, and set viewport), so off means observe-only; allowNavigation gates navigate, history, open pane, pop out, and dock, each of which loads or reloads a page; and allowEval gates eval (off by default). A fifth gate, restrictNavigationToLocalhost (off by default), confines navigation to localhost and private hosts when enabled.

Two rules decide which tab a browser tool acts on, and both are worth reading before you pass a sessionId. Both have held since v0.38.0.

sessionId is a surface handle, not an agent session id. It looks like pane_<8 hex> for a visible pane or lane_<8 hex> for an offscreen one, and you get it from kangentic_browser_open_pane or kangentic_browser_list_panes. A handle names exactly one tab for that tab’s whole life: re-registering the same tab keeps it (a /clear rotating the owning session does not change it), and a new tab always gets a new one, so a handle can never quietly retarget to a different page. Passing something that was never a handle - an agent session id, for instance - is refused as no-pane-open.

When the tab behind a handle is gone, the call fails with surface-gone rather than being redirected. The error says why the tab went, how long ago, that per-tab state (sessionStorage, in-memory app state) did not carry over while cookies and localStorage did, and which surface to use instead.

Omitting both targets your own task only. For a caller bound to a task, resolution ranks that task’s surfaces - the visible pane first, then the offscreen form of the same surface - and refuses with multiple-panes (carrying the candidates) when two share the top rank, or no-pane-open when the task has none. It never falls through to another task’s pane, however many are open in the project: that fall-through was observed navigating a sibling task’s signed-in app to an identity-provider URL. Only a caller with no task - a human-driven client, a Command Terminal, or the two-segment .kangentic/mcp-config.json URL - uses the project-wide rule of “a surface my own session owns, else the single pane open in the project”.

Every driving tool can therefore return surface-gone alongside the older shared refusals: no-pane-open, multiple-panes, foreign-project, pane-destroyed, pane-not-rendering, cdp-attach-failed, pane-busy, and driver-error.

List Browser Panes - list the open embedded Browser surfaces in your project, with their handle (sessionId, the value to pass back), the agent session that owns them, taskId, kind (pane for the visible pane, lane for the offscreen form of that same one surface), current URL, and whether the surface is alive and debugger-attached. Use it to discover a target for the other tools, or to confirm the user has a dev server loaded. Panes in other projects are excluded by default; pass includeOtherProjects to see them too, though they are listed for visibility only and cannot be driven. Returns an empty list when no pane is open.

Each surface also carries visibility: showing (the user can see it), hidden (the user put it away behind the terminal with the Browser pill), parked (the user closed its window), or offscreen. Every value is still driveable - the field says whether the user can watch what you are doing, not whether you may act. A pane the user closed with Close browser is not listed at all; its handle answers surface-gone.

Open Browser Pane - open the Browser pane for your own task and load a URL, so you can then drive it with the other kangentic_browser_* tools. Use this instead of asking the user to click the Browser pill. It opens the task’s detail window if it is not already open, and returns the pane once it is registered and driveable, so the very next call can act on it.

It shows a hidden pane again. A pane the user hid with the Browser pill, or whose window they closed while you were live, is still registered and driveable, so the call takes the warm path and additionally asks the app to surface it: same tab, nothing reloaded, and the window it raises is agent-stamped so its terminal never steals the user’s keyboard. Only close_pane discards. The visibility on a warm response is a snapshot taken before that re-surface is applied, so call list_panes afterwards if you need the settled value.

url is the whole input, and it is optional: omit it to reuse the task’s saved Browser URL or the project default. If neither exists, pass one, because a pane with no URL registers nothing and cannot be driven. Calling it again with a different url navigates the existing pane rather than reopening it. This tool only ever targets your own task; there is no way to open a pane for another task or project. It is gated by allowNavigation, not by a separate gate. There is nothing else to choose because a task has exactly one browser surface: the isolated: true argument that opened a private lane was removed in v0.43.0, and One surface per task says why.

A backgrounded project gets the surface offscreen instead of a refusal. The board window layer renders only the open project’s tasks, so no pane can mount for a project that is not the one on screen. The response then carries offscreen: true and a laneId, the surface is fully driveable, and the task card still shows that a browser exists. Opening the Browser pill later reclaims it: the pane mounts at the offscreen surface’s current URL, that surface is destroyed, and the agent’s old handle answers surface-gone naming the replacement. The page reloads in the move, so sessionStorage is lost while cookies and localStorage survive, the same trade pop_out and dock make.

Refusals worth knowing beyond the shared ones: no-caller-task (the connection is not bound to a task), browser-pane-disabled, task-not-found, no-url (neither fallback is readable, which is always the case for a backgrounded project, so pass one), surface-opening (the task’s surface is loading its first page, so retry), surface-exists (a second surface for one task, which both callers prevent), lane-load-failed (the offscreen surface’s first URL did not load in time, so it was destroyed rather than left half-open), lane-swept (the open raced a teardown, worth one retry), app-not-ready, and url-seed-failed.

Close Browser Pane - discard Browser panes, exactly as the user’s Close browser control does. This is not what the Browser pill does: the pill only hides a pane, keeping the page loaded for you. The task’s detail window stays open. With no arguments it closes your own task’s pane; pass all to close every pane in your project, which takes precedence over sessionId and taskId. Panes in other projects are left alone and reported as a count unless you pass includeOtherProjects, which is off by default because another project may have an agent mid-verification in its pane. The response lists the panes actually closed, so a partial close is never reported as complete. A pane you close this way stands up no offscreen replacement.

Navigate - point a task’s Browser pane at a URL. Gated by allowNavigation.

History - go back or forward in the pane’s history, the way its own arrows do. Gated by allowNavigation. It returns the URL read after the navigation commits rather than the one it started from, and refuses with no-history when there is nowhere to go, instead of quietly doing nothing.

Screenshot - capture the Browser pane as an image.

If the page is showing a dev-server build-error overlay, the call returns a dev-server-error describing the failure instead of a picture of the red rectangle. The check runs inside the same drive as the capture, so the two can never disagree about what was on screen.

A pane’s screenshot holds no more pixels than the pane has, which is its width times its height times the display’s scale. That limit is Chromium’s, not a setting. Asked for more, Chromium repeats the pane’s pixels to fill the difference. So every capture of a docked or popped-out pane is scaled down to fit instead. A 1600x1000 viewport in a 740x749 pane comes back 740x463 on a 100% display, and about 1480x926 at 200%. fullPage is scaled to fit the same way, so a long page comes back small. An offscreen surface is a real window that Chromium can grow, so it is not bounded. If a capture ever comes back larger than its pane anyway, the call fails and names the pane’s size rather than returning the tiled image.

The response carries pixelsPerCssPixel, the number to divide an image coordinate by for the page’s CSS one. It is not always deviceScaleFactor, the page’s own ratio. A capture scaled to fit its pane or maxBytes holds fewer pixels than the page renders at. A note appears when the pane is the reason a capture is small.

Screenshot Element - capture a single element in the Browser pane at up to 1:1: one image pixel per CSS pixel, or the page’s own ratio when that is higher. The element is re-rendered at that density even when the page is zoomed out to fit a wide viewport, so this is the tool for reading detail in a desktop-width layout on a narrow pane. An element too large for the pane at that density comes back scaled to fit, with a note.

Query DOM - get the HTML and box model of an element.

Query All - measure every element matching a selector in one call.

Bounding Box - the CDP box model of an element. Its quads are page coordinates and do not account for scroll, while kangentic_browser_click takes viewport coordinates. Do not feed one into the other; click by selector instead, which scrolls the element into view and measures it there.

Browser Console - read recent console messages from the pane.

Network Requests - list the requests the page has made, each with its method, URL, resource type, status, error text, and duration. Filter with urlContains, keep only failures and 4xx/5xx with failedOnly, and cap the count with limit (50 by default, 300 at most). The console shows only what the page chose to log, so “did that POST fire, and what came back” had no answer before this. A request still in flight is listed with a null status rather than hidden, because a dev server that accepted the connection and went quiet is usually the thing being looked for. The ring holds the last 300 settled requests for the surface.

Wait - wait for an element or text to appear. Each poll takes the pane on its own rather than holding it for the whole wait, so a long wait never blocks another agent’s click or screenshot. A refusal (pane gone, busy, or policy) ends the wait immediately instead of retrying to the deadline.

Click - click an element or a point in the pane. Gated by allowInteraction. With coordSpace: "image", x and y are pixels of a full-viewport screenshot, mapped back through the pixelsPerCssPixel that screenshot reported. That holds only when the screenshot’s scale was 1. For one that maxBytes shrank, divide by its own pixelsPerCssPixel and pass viewport coordinates instead.

Hover - move the pointer over an element without pressing, so a hover menu opens, a tooltip appears, or a hover-revealed control renders. Gated by allowInteraction. It is not a step before clicking, since kangentic_browser_click already moves the pointer before it presses; it exists so hover state can be screenshotted, which nothing else could reach.

Type - type text into the Browser pane. Gated by allowInteraction. Pass a selector and the element is clicked first, in the same call, so it holds keyboard focus; clearFirst then selects all and deletes before typing. A newline in text presses Enter, so {selector, text: "query\n"} types and submits in one call.

Without a selector the text goes to whatever the page has focused, and only while the pane itself holds keyboard focus. Between calls that is usually your terminal, and a key sent then would land there, so since v0.43.2 every key is checked first and the call refuses with pane-not-focused instead. Keys sent before a refusal were delivered.

Keypress - send one key or chord to the pane. Gated by allowInteraction. Pass a selector to click the element that should receive the key first, in the same call; an <iframe> selector works, since the click lands inside the frame. Without one the call refuses with pane-not-focused unless the pane already holds keyboard focus, for the same reason as kangentic_browser_type. An agent’s Escape that reached your terminal interrupted the agent that sent it. A combo that cannot be parsed refuses with unknown-key before anything touches the page, so a typo never costs the click.

Enter submits a form or starts a new line the way a real Enter does. The navigation keys are delivered to the page without the browser’s own default action, so PageDown and End reach a page that handles them itself and do not scroll the document. Use kangentic_browser_scroll for that.

Scroll - scroll the page, or one scrollable element, by a wheel delta in CSS pixels. Gated by allowInteraction. Positive deltaY scrolls down; pass a selector to scroll a particular scroller. This is the only way to reach content below the fold: a keypress does not carry the default scroll action, and eval is off by default. With no selector the wheel is aimed at the centre of the viewport, exactly as a real wheel is, so a scroller sitting under that point takes the scroll instead of the page. It returns the viewport measured after the scroll, and refuses invalid-scroll when both deltas are zero.

Drag - drag from one element to another. Gated by allowInteraction.

Select Option - choose an option in a native <select> by value, label, or index (exactly one of the three). Gated by allowInteraction. Clicking cannot do this: the list a <select> opens is drawn by the operating system outside the page, so a synthesized press reaches the control and then has nothing to aim at. The tool fires input and change, so framework bindings react as they do for a real choice. A custom dropdown built from divs is ordinary markup and wants kangentic_browser_click instead.

Drop Files - drop real files onto an element, as dragging them out of the file manager would, from 1 to 20 absolute paths on this machine. Gated by allowInteraction. The page receives genuine File objects through dataTransfer.files, each backed by its real path, which is the one hop page script cannot fake, so this is the only way to exercise a drop zone or a file input end to end.

Handle Dialog - decide how the page’s alert, confirm, prompt, and beforeunload dialogs are answered, and read the ones already raised. Gated by allowInteraction. Arm it before the action that triggers the dialog: a dialog blocks the renderer while it is open, so a call made afterwards would hang rather than answer it. The arm is consumed by the next dialog unless you pass persist.

Every dialog is answered whether this is called or not, which is what stops a pane wedging. Kangentic routes dialogs through the debugger rather than Chromium’s own UI, so one nobody answered would block the page with nothing on screen for the user to dismiss. The default is dismiss, the safe direction for all four types. prompt() is the exception: Electron does not implement it, so it throws in the page rather than opening a dialog, and promptText is unreachable for a pane today.

A docked pane is only as wide as the task window’s split leaves it, which in practice sits below every desktop breakpoint. An agent verifying a responsive layout there measures the mobile rendering and reports it as the desktop one. Three tools exist so the viewport can be chosen deliberately, and they are not interchangeable. What separates them is what each costs the page:

MechanismViewport controlPage stateSurface handle
set_viewport on a docked paneAny width and height, including larger than the physical screenKept. No reload, so sessionStorage, in-memory state, scroll, and sign-ins all surviveUnchanged
set_viewport on an offscreen surface or an already-detached windowReal pixels, capped by the display for a window and uncapped offscreenKept, because nothing movesUnchanged
pop_out or dockNot a viewport control, a relocationLost. A fresh pane, so the page reloads and sessionStorage goes. Cookies and localStorage survive, since the jar is keyed by taskNew. The old handle answers surface-gone naming the replacement

So set_viewport on the surface you already hold is the route to any resolution mid-flow, and pop_out is what buys real OS pixels and a window the user can drag, at the cost of the page’s in-memory state.

Asking a docked pane for a size zooms it out to fit, so the user sees the whole layout rather than its left third while the pane’s chip claims a width the window cannot show. Pass zoom: 1 to show the user a 1:1 crop instead. Screenshots are the whole viewport either way, at no more pixels than the pane has (see kangentic_browser_screenshot), so a fitted 1600-wide layout in a 740-pixel pane comes back about 740 wide. For detail, screenshot_element captures a region at up to 1:1 even on a fitted page, and pop_out gives a real window whose viewport screenshots are 1:1. A popped-out pane is still a pane, so a full page or an element larger than its window is scaled to fit the same way.

Set Viewport - set the viewport a surface lays out against and report the one it actually got. Gated by allowInteraction. width and height run from 128 to 8192 CSS pixels; zoom and a nine-point position for a detached window are optional, and reset drops the override and restores the zoom the fit changed. deviceScaleFactor is optional too. It is the devicePixelRatio the page sees, for testing hidpi assets, and applies to docked panes only. Leave it out otherwise. The page then sees the display’s own ratio scaled by the zoom, like any zoomed page, and an emulated ratio can make a canvas sized from device pixels draw blank.

The returned viewport is measured from the page after the change settles, never the request echoed back, and exact is false when the two differ by more than a couple of pixels: a window loses its frame and the pane’s chrome to the viewport and is capped by the display, and an offscreen surface can be clamped. The returned deviceScaleFactor is measured the same way, from the page. display is the work area of the display the surface is on, and maxWindowViewport is the largest viewport a real window there can reach, null except on the window path. mechanism names which of the three paths ran, chosen from the surface rather than from an argument. An override is scoped to the drive session rather than the document, so it survives a navigation and ends on reset, when the agent’s session ends, or when the user clears it from the pane’s viewport chip. Beyond the shared refusals it returns invalid-viewport for a dimension out of range.

Pop Out Browser - detach the caller’s own task Browser pane into its own OS window, optionally at a width and height, maximized, or at a nine-point position. Gated by allowNavigation, since the move reloads the page. It takes no sessionId or taskId, for the same reason open_pane does not.

Use the sessionId it returns from then on: the window mounts a fresh pane, so the previous handle is dead. Called while a window is already open it resizes that window and reports moved: false rather than opening a second one. The window is shown inactive, so an agent action never takes the user’s keyboard focus, and a size the agent applies is not saved as the user’s, since pop-out bounds persist per surface kind rather than per task. Refusals beyond the shared ones: no-caller-task, no-pane-open, detach-failed, and detach-timeout past 20 seconds.

Dock Browser - put the caller’s detached Browser window back into the task, undoing kangentic_browser_pop_out. Gated by allowNavigation, takes no parameters, and returns the same shape. Docking moves the page too, so it reloads and mints another handle: use the sessionId this returns. It refuses not-detached when the pane is already in the task window.

Eval - evaluate a JavaScript expression in the loaded page. Off by default; gated by allowEval. An expression that has not settled after 20 seconds is abandoned and reported as an evaluation error, so a promise that never resolves cannot hold the pane against every other caller.

Read-only tools for inspecting Kangentic itself. This group is dev-leaning and renders last in the panel.

Query Database - run a read-only SQL query against the project database. The connection uses PRAGMA query_only = ON, so writes are rejected.

Tail Logs - read recent lines from the Kangentic console log. The project’s log is merged with the app’s global fallback (<configDir>/logs/, where the log mirror writes while no project is open, so a global subsystem such as the mobile bridge keeps its trace across the gap), sorted by timestamp before the filters apply. Errors and warnings are always captured; info, debug, and log levels only when developer.persistConsoleLogs is on.

Recent Crashes - list recent crash records with source-mapped stacks and the version captured at crash time, falling back to the app’s own config directory for a crash recorded with no project open. Always on, no toggle required.

Process Metrics - a live snapshot of memory and CPU per Electron process (main, renderer, GPU, utility) plus version and uptime. On Windows each row’s privateBytes is the commit figure, which is what the low-memory warning measures, and the two model workers appear under their own service names. The main entry also carries the main process’s own process.memoryUsage() in bytes, the only way to separate V8 heap from Node’s external allocations from Chromium’s own footprint without attaching an inspector to a live process.

IPC Log - recent IPC traffic with per-call timings and errors. Available only when developer.recordIpcTraffic is on; channels carrying secrets are redacted.

List Worktrees - enumerate worktrees for one or every project, each with branch, the base branch its work is based on, dirty flag, commits ahead/behind that base, and last-commit time. It never fetches: the counts are as current as the remote-tracking refs, which the background auto-fetch keeps refreshed for the focused project.

Kangentic is free and open source. A star helps other people find it.

Star on GitHub