Skip to content

MCP server

Axowl speaks Model Context Protocol over Streamable HTTP at https://testapi.axowl.com/mcp. Point your own agent runtime at it and the agent acts under its badge — Axowl issues and enforces the authority, your runtime does the work.

This is the primary way to run an agent. The Axowl console harness is the fallback for teams with no runtime of their own.

In the dashboard, open your app group’s Agents tab and press the plug icon on the agent you want to run. Axowl issues a credential bound to that agent’s badge — expiring when the agent does — and hands you the exact command:

Terminal window
claude mcp add --transport http axowl https://testapi.axowl.com/mcp --header "Authorization: Bearer ah_live_..."

The credential is shown once and cannot be retrieved afterwards; issue a new one if you lose it.

To issue it yourself instead, POST /api/org/{slug}/settings/keys/api-keys with connectedIdId set to the agent’s badge. Either way it must be bound to an agent badge — a key with no connectedId, a human badge, or a badge that is suspended, revoked, or past its TTL is refused with a sentence explaining which.

If your client speaks OAuth — claude mcp add --transport http axowl https://testapi.axowl.com/mcp with no header — it discovers the authorization server, registers itself, and opens a browser. You sign in to Axowl, pick which agent it may act as, and approve. The token it receives names that one agent and names you as the delegating person.

This is the better path when a person is present, because the delegating person is whoever actually signed in, rather than whoever happened to issue a key months ago.

Under the hood: RFC 9728 protected-resource metadata, RFC 8414 server metadata, RFC 7591 dynamic client registration, authorization code with PKCE (S256 required), and rotating refresh tokens. Access tokens last an hour.

An agent that is suspended, revoked, or past its TTL stops working immediately — the badge is re-checked on every call, not at token issuance. Revoking does not wait for a token to expire.

An agent’s authority is what the agent holds ∩ what the person who delegated it holds.

The delegating person is whoever signed in (OAuth) or whoever issued the API key. This is not a formality — a request with no delegating person is refused outright, because an agent cannot act on nobody’s behalf. If you see every tool refusing, that is the reason, and axowl_whoami says so directly.

The consequence worth internalising: widening the agent alone will not lift a refusal that names the delegating user. A person cannot hand a machine authority they do not have themselves.

Axowl does not build your application. It hands the agent the materials already attached to the app group and enforces what it may do with them:

app group ─┬─ connected repository (code) ──> read it, propose a pull request
└─ attached database (data) ──> read the schema, run SELECTs

Connecting the repository and the database is a person’s job — a prerequisite to hiring, not something the agent does. The agent gets read access to both, and the only way it changes code is a pull request.

ToolWhat it doesGate
axowl_contextApp group, connected repo, attached DB, and what this agent may donone
axowl_whoamiBadge, agent, app group, TTL, and who delegated itnone
axowl_list_permissionsEvery concrete scope on the badgenone
axowl_check_permissionAsk about one scope before attempting itnone
axowl_repo_list_filesPaths in the connected repository, on any branchorg.git_repository.read
axowl_repo_read_fileRead one file, on any branchorg.git_repository.read
axowl_repo_pull_requestsYour open pull requests, their branches, CI results and review commentsorg.git_repository.read
axowl_repo_ci_logWhy CI failed — the last lines of the failed job’s logorg.git_repository.read
axowl_db_schemaTables and columns of the attached databaseorg.data_source.read
axowl_db_queryRun a SELECT and get rows backorg.data_source.read
axowl_propose_code_changeOpen or continue a pull request with whole-file changes and deletionsorg.git_repository.propose_change
axowl_approval_formsThe approval forms this org defines, what each grants, who would stamporg.work_item.list
axowl_request_permissionRefused a scope? Submit the approval that grants itorg.reporting_instance.create
axowl_approval_submitSubmit any approval document (access or plain document approval)org.reporting_instance.create
axowl_approval_statusYour submitted documents and each stamp boxorg.reporting_instance.read
axowl_request_workflowAsk for a deployment workflow to be installed — a person stamps the exact fileorg.reporting_instance.create
axowl_request_pages_projectAsk for a Cloudflare Pages project on the customer’s own account (you choose the name only)org.reporting_instance.create
axowl_request_migrationAsk for a schema change on staging or production — a person stamps the exact SQL, Axowl runs itorg.reporting_instance.create
axowl_repo_secretsWhich repository secrets the deploy needs, and which are set (names only)org.git_repository.read
axowl_docs_listAxowl’s own documentation — every page, by sectionplatform.doc_page.read
axowl_docs_readRead one documentation pageplatform.doc_page.read
axowl_pm_storiesThe stories assigned to you across this app group’s projectsassignment
axowl_pm_storyOne assigned story in full — description, checklist, recent activityassignment
axowl_pm_moveStart a story (InProgress), send it for testing (InReview, with a test guide), or give it back (ToDo)assignment
axowl_pm_commentLeave a note on one of your storiesassignment
axowl_pm_taskAdd a sub-task, or tick one offassignment
axowl_pm_create_storyFile something you found mid-flight into the backlog, or (forPerson) ask a person for setupassignment
axowl_pm_linkAttach a pull request, commit or branch to one of your stories (not a preview — Axowl attaches that)assignment
axowl_pm_filesThe documents in a project’s file box — specs, decisions, notesproject
axowl_pm_fileRead one of those documents, as textproject
axowl_pm_epicsThe epics across your projects, with their state and story countsproject
axowl_pm_propose_epicPropose an epic — it lands in Backlog for a person to acceptproject

Start with axowl_context. It tells you what is connected and which of the above will actually work, so you find out before you try rather than after.

Project work: assignment is the permission

Section titled “Project work: assignment is the permission”

The axowl_pm_* tools carry no scope of their own. What opens them is being assigned the story — a person puts your badge on it on the project board, and from then on you can read and move that story and nothing else. Three conditions hold before any of them answer:

  1. the project belongs to your app group,
  2. the person who delegated this session can read that project,
  3. that app group is connected to Project Management AI (a paid add-on).

Fail any of them and the tool says so in a sentence. A story you are not assigned to answers the same way whether it exists or not — knowing an id must not tell you a project’s shape.

Two of the tools work at the project level rather than the story level. axowl_pm_epics and axowl_pm_propose_epic need the three conditions above but no assignment, because proposing work is how the assignment gets created in the first place. Planning happens here, on your tokens. A person writes a brief in the project wizard; it arrives as an epic in Backlog. You read it, check what already exists so you do not propose the same thing twice, and file epics of your own. Leave the owner unset — you cannot assign work to yourself, and an epic stays in Backlog until a person moves it. Axowl runs no model of its own for this.

One story at a time: write, test, deploy, done

Section titled “One story at a time: write, test, deploy, done”

Stories move through the board one at a time per agent and project:

ColumnWho moves it thereWhat happens
To doA personWaiting its turn
In progressA person presses Start, or Axowl opens the next one when the previous is doneYou write it: branch, pull request, CI
TestingYou, with axowl_pm_move(state: "InReview", testGuide: …)A person follows your test guide
DeployingA person presses PassYou are woken to call axowl_request_merge; a person stamps it
DoneAxowl, when that merge goes throughThe next story opens

You cannot start a second story while one of yours is in progress, testing or deploying, and you cannot move a story to Deploying or Done yourself.

Moving to Testing needs three things, and Axowl checks them rather than taking your word: the pull request attached with axowl_pm_link, its CI green, and a test guide. Write the guide in markdown with three headings. ## Setup is what the person prepares first (a test organization, an account, some data), with where in the console to do it. You do not do the setup yourself, and you never put a password, key or token in the guide. ## Check is numbered steps, each saying what they should see. ## Not in this story is what they should not expect yet.

When the story reaches Testing, Axowl attaches the preview address for that branch to the card, so the person opens the running result rather than a list of code changes. The address comes from the GitHub Deployment your repository’s deploy workflow made (environment: preview), read by Axowl — you cannot write it: axowl_pm_link refuses a preview kind, because an address you typed would not have to be the thing that was deployed. A repository with no Axowl deploy workflow has no preview and is tested from the pull request; ask for one with axowl_request_workflow.

Previews and deploys land on the customer’s own Cloudflare account, connected once by a person in the console. Until it is connected there is nowhere to publish — Axowl has no account of its own to fall back to — so a repository with no connection gets a refusal saying exactly that. If the Pages project itself does not exist yet, ask for it with axowl_request_pages_project; you choose the name, a person stamps it, and Axowl creates it on their account.

Redo, Back to backlog and a completed merge delete the agent branch, and the preview goes with it — it is a public address and does not outlive the story. Request revision does not: you keep working on the same branch and pull request.

From Testing only a person moves the card, with one of four buttons. Each wakes you with its own reason, and axowl_pm_story shows the decision and its note at the top:

ButtonCard goes toreasonWhat you do
PassDeployingstory_passedAsk for the merge
Request revisionIn progressstory_reviseFix it on the same branch, send it back with a fresh guide
RedoIn progressstory_redoStart a new branch, send it back with a fresh guide
Back to backlogBacklogstory_holdStop; the person is changing the story itself

axowl_request_merge refuses a pull request whose story has not been passed. If the merge is refused, fails or its approval is rejected, the card goes back to Testing and the person is told.

When the project needs something only a person can make before you can go on, such as a connected Cloudflare account, a secret or a test organization, call axowl_pm_create_story with forPerson: true. It becomes a setup story assigned to the person who tests your work, and you are woken with setup_done when they move it to Done. Builds, deploys and hosting run on the accounts the customer connects, never on Axowl’s own credentials.

You are woken when work arrives: assignment and state changes fire agent.wake with reason story_assigned / story_state_changed (plus the test reasons above, and story_start when a story is opened for you), and axowl_context lists your queue at the top of every session. When your last story closes, file a completion approval with axowl_approval_submit: finishing and archiving a project is a person’s stamp, not yours.

Project documents live in the file box, not in the repository

Section titled “Project documents live in the file box, not in the repository”

A repository holds code. Specs, decisions, pricing and drafts belong in the project’s file box, because a deploy publishes a repository and those documents are not meant to be public. People upload them on the project’s Documents tab; you read them with axowl_pm_files and axowl_pm_file, alongside the project summary, your epic and your story.

You cannot upload, change or delete a document — a person manages what is in there. Reading is limited to text files (markdown, text, json, yaml, csv, sql, xml, html, css, js, ts) up to 512KB, trimmed if long; anything else is refused rather than dumped into your context. A document from a project you cannot see answers the same way whether it exists or not.

The database is read-only to you, and a pull request containing a migration is refused whole — code can be reverted, data cannot. axowl_request_migration(sql, reason) is the way through: it files an approval whose facts block holds the SQL in full, plus a warning line counting any statements that destroy data. Once a person stamps it, Axowl runs it against the database attached to your app group — whether that is one Axowl manages or one the customer connected — in a single transaction. If any statement fails, nothing is applied and the reason comes back on the approval.

Two things follow from that. Write SQL a person can read in one sitting; anything over 20,000 characters is refused for that reason alone. And propose the code that depends on the change in the same breath — a column nothing reads is as broken as a reader with no column.

If the connected database’s account has no permission to change structure, the database refuses and the approval says so; a person then reconnects with an account that can, or grants it.

Schema changes go to staging first when the app group has a staging database. Call axowl_request_migration with target: "staging" (that is the default when staging exists), have a person stamp it, then file the production one. Axowl checks that the exact same SQL already ran on staging before it applies anything to production, and refuses when it has not — a stamp does not override that check. A person marks which connection is staging in the console; you cannot.

Discarding your work (a person pressing Redo or Back to backlog) does not undo what already ran on staging — migrations only go forward. Axowl notes on the story what was left behind; reversing it is another approval, with the reversing SQL written out.

Deployment workflows: you choose a template, a person stamps the file

Section titled “Deployment workflows: you choose a template, a person stamps the file”

.github/ stays off limits to you — a workflow you could edit is a workflow that could deploy your own code without anyone looking. But a project still needs one, so the file arrives the same way a merge does: you ask, a person stamps, Axowl’s server does it.

axowl_request_workflow() with no arguments lists the templates Axowl offers. Call it again with the template, the Cloudflare Pages project name and the folder (or .csproj) to publish, and it files an approval whose facts block contains the whole file, so the person stamping reads exactly what will be written. On approval Axowl commits it to the default branch.

What the templates guarantee, and why:

  • the build job has no secrets — that is where your code runs;
  • the deploy job does not run your code, it only uploads the artifact the build produced;
  • deploy secrets hang off GitHub environments, and production only from the default branch;
  • every push of an agent branch gets its own preview URL, which Axowl reads back from the deployment.

The workflow runs on the customer’s GitHub Actions minutes and publishes with the customer’s own Cloudflare account — Axowl’s credentials are not used for your project’s builds or deploys. The repository secrets each template needs are named in the result. You cannot set them, and you must not ask anyone to paste one to you: file a setup story instead with axowl_pm_create_story(forPerson: true), and a person enters the value where it belongs.

Publishing the repository root is refused — name the folder. A repository often holds documents that are not meant to be public.

Read the documentation before you wire anything to Axowl

Section titled “Read the documentation before you wire anything to Axowl”

axowl_docs_list / axowl_docs_read serve this documentation site through the tool surface, bundled with the server so the pages always match the version you are talking to. Every agent template carries the scope, so this works out of the box — it appears on the role because everything an agent can do should be visible there, not because we expect anyone to withhold it.

They exist because an agent that cannot read the platform’s own documentation will infer how the platform behaves from the repository in front of it, and quietly get it wrong. A sign-in URL invented that way looks perfectly reasonable and is simply not a real address. So: when the task touches Axowl itself — sign-in, permissions, seals, billing, organizations — read the page first and say so if it isn’t there. Guessing is the failure mode this tool removes.

axowl_db_query runs SELECT and nothing else — that is the definition of the call, not a convention layered on top. Writes go through a separate path that passes an approval gate.

A failing query returns the database’s own error message, so your model can correct the statement and try again.

Code changes are pull requests, never pushes

Section titled “Code changes are pull requests, never pushes”

axowl_propose_code_change takes whole files, not patches, and opens a pull request against the repository connected to this agent’s client. There is no direct-push path: merging is the human step. Three things are checked before anything is written — the badge’s authority, the paths (CI configuration and secrets are off limits, because an agent that can edit the pipeline can deploy itself), and the size (20 files, 512KB per pull request). Each rejection is reported individually.

Set "delete": true on an entry to remove that path. Deletions pass exactly the same path filter — an agent that cannot edit .github/ cannot delete it either.

Real work does not fit in 20 files, so a proposal is built up over several calls on one branch. The first call returns a branch name; pass it back as branch on every following call and the changes stack onto the same pull request instead of forking fresh copies of the default branch.

axowl_propose_code_change(prompt, files) → Opened a pull request with 20 file(s)
branch: axowl/agent/20260826-101500
axowl_repo_read_file(path, "axowl/agent/20260826-101500") → (what you just wrote)
axowl_propose_code_change(prompt, files, "axowl/agent/…") → Added 18 change(s) to the existing PR
axowl_repo_pull_requests(number) → state, CI, review comments

Only agent branches (axowl/agent/…) can be continued. Naming the default branch is refused — that would be a direct-push path by another name, and merging stays the human step either way.

axowl_repo_pull_requests closes the loop: it says whether a proposal was merged (so its files are already on the default branch), whether it still merges cleanly, what CI concluded, and what reviewers wrote. Without it a second round is written blind.

An agent in Axowl is an employee, and employees do not give up when a door is locked: they file a request. axowl_request_permission(scope, reason) finds the Access Approval form that grants the scope, submits it under the agent’s badge, and routes it to the people who stamp it. When they approve, the scope lands on the badge — call axowl_check_permission again and carry on.

axowl_propose_code_change → code — denied (agent lacks org.git_repository.propose_change)
axowl_request_permission → submitted acme-2026-000031 — 1 stamp needed: Jane Park
(stamped in the dashboard with a passkey)
axowl_check_permission → allowed — org.git_repository.propose_change

Three rules keep this honest:

  • The agent only submits. People stamp. There is no stamping tool; approval is a passkey act in the dashboard. An agent’s badge credential is what proves the submission is the agent’s own.
  • Machines never self-approve. A form with risk level 0 is auto-approved when a person submits it (routine work). When an agent submits it, the line is raised so that at least one person in the agent’s reporting line has to stamp — and if nobody there holds what the form grants, the document stays submitted and the tool says so. This is enforced where the approval line is derived, so it holds for any client, not only MCP.
  • Approval grants the badge, not the delegating person. If the refusal named the delegating user, axowl_request_permission tells you approval will not help and who has to obtain the scope first.

The agent’s manager is the person who hired it — hiring creates that reporting line, and the org chart can move it. The organization also has to define the form: if no Access Approval form grants the scope, the tool asks you to tell the delegating user which scope to set up under Forms & Policies.

Every kind of agent holds the scopes to list forms and submit documents (org.work_item.list, org.reporting_instance.create, and their read counterparts). Even a test agent — submitting a request is asking, not writing.

Each write is recorded as a turn in a console thread named after the agent, so “what has this agent done” stays one screen. Refusals are sealed as agent.action_denied audit events; a pull request is sealed as agent.code_proposed; a submitted approval is a numbered, sealed document like any other — it shows up in Requests & Approvals with the agent as drafter.

Retries are safe: an identical call is answered once

Section titled “Retries are safe: an identical call is answered once”

Agent runtimes retry a tool call that timed out. Every write tool therefore treats an identical call from the same badge within 24 hours as the same action and returns the first result again, prefixed with (replayed — …), instead of doing it twice: no second pull request, no second approval document, no second question email, no duplicate story, link or note, and a checklist toggle does not flip back. If the first call is still running when the retry arrives, the retry is told to wait and read the result with the matching read tool.

“Identical” means the same arguments. Each write tool also takes an optional idempotencyKey: pass any string that names this one action and Axowl recognises the retry by that key even if you reworded something; pass a new key to deliberately repeat an identical action. Refusals and errors are never replayed — once you have the permission, or the database is back, the same call runs for real. This applies to axowl_propose_code_change, the approval tools (axowl_approval_submit, axowl_request_permission, axowl_request_merge, axowl_request_migration, axowl_request_workflow, axowl_request_pages_project, axowl_request_deploy_target), axowl_ask, and the project tools (axowl_pm_move, axowl_pm_link, axowl_pm_comment, axowl_pm_task, axowl_pm_create_story, axowl_pm_propose_epic).

The two are deliberately different:

  • No credential at all is a protocol problem. /mcp answers 401 with a WWW-Authenticate: Bearer resource_metadata="…" header, which is what lets a client discover the authorization server and start signing in.
  • A credential that lacks authority is a modelling problem. The tool returns 200 and the reason as its result — boards — denied (delegating user lacks org.record_schema.read). Your model reads that, understands what it lacks, and can ask a human to fix it. This is the same contract the Axowl console harness uses: a denial goes back to the model, never silently swallowed.
  • No database writes. Reading is open; changing rows is not.
  • No stamping. An agent can submit an approval but never approve one — by design, not omission.
  • One repository per agent. The agent reads the repository connected to the client it owns. If a client has none connected, axowl_context says so.
  • No merging, and no replying on a pull request. An agent reads review comments and CI results; answering them means proposing another change on the same branch.
  • No running the code. Build and test results reach the agent only through CI on the pull request — an agent cannot edit CI configuration, so a person sets that up once.
  • No self-assignment, and no archiving. An agent cannot put itself on a story, take itself off one, or finish and archive a project. Assignment is what grants it work, so granting it to itself would be the same as having no gate; archiving writes a snapshot and cannot be undone, so it stays behind a human stamp.

See also Agents and Roles & permissions.