mcptask runner – AI developer on your machine. It finishes tasks to merged PRs.
mcptask_runner is the part of the AI coding agent orchestrator that runs on your own machine — mcptask.online holds the task queue, the runner does the work. It turns your coding CLI — Claude Code, Codex CLI or OpenCode — into a self-driving teammate. It works with Claude, Kimi, DeepSeek, local ollama, or any model you choose — no vendor lock-in. It picks the task, writes code and tests, and opens a PR. In auto-squash modes it merges when CI goes green; in manual modes it leaves the PR open for you to review. Assign well-specified work, get a green PR.
mcptask_runner does not replace your team — it multiplies it.
The runner takes its tasks from mcptask.online — its only source. It does not read Jira, Linear or GitHub Issues directly.
Install with one command:
curl -fsSL https://github.com/jchsoft/mcptask-releases/releases/latest/download/install.sh | shOn macOS, also with Homebrew:brew install jchsoft/tap/mcptask_runner
Mac falls asleep mid-task? Keep it awake only while the coding CLI runs: setlauncher.command: [caffeinate, -i, claude]in config/mcptask_runner.yml.How the scheduled job is set up
Free 30-day trial. No credit card. Cancel any time.
You pay for the runner like for any other user
mcptask_runner is a user of your account, and the tariff counts it exactly like anyone on your team — no special line item, no discount, no surcharge.
Model tokens are paid from your own Claude, Codex or OpenCode subscription. mcptask.online does not bill for them.
On your machine, against reality
Cloud coding agents run in sanitized sandboxes. The runner runs in your actual project.
Real database
It talks to the database your project already uses — the schema, the seed data, the migration history that shaped both. (PostgreSQL is one worked example; whatever your project runs is what the runner drives.)
Real system tests
Your project's own test entry points run, end to end — Capybara/Selenium against your real app as one worked example, with the screenshots and browser checks that prove the change actually shipped.
Real commits and PRs
Branches, commits, and pull requests live in your repo, with your CI, under your Git history — on GitHub, on Bitbucket Cloud or on GitLab, all opened through `mcptask_runner pr`. Whatever your `bin/ci` runs is what the runner runs.
A full autonomous harness around your coding CLI
mcptask_runner is one static binary with no runtime dependencies — it drives a coding CLI on your machine and runs the whole agent loop: pick a task → triage it → choose a model → run the coding CLI → watch the run with a watchdog → respect the daily quota → (optionally) merge the PR after CI. It works with any project that uses git, on GitHub, on Bitbucket Cloud or on GitLab — nothing in the execution path needs Ruby, bundler or a Gemfile, project metadata comes from CLAUDE.md, and `bin/ci` is a convention (no `bin/ci` → skip). No prompt it composes names a framework's command any more: a project that needs preparing before its browser suite declares that itself — in its CLAUDE.md, or in the command it hands `/test-runner`.
Six builds per release — macOS, Linux and Windows, each on amd64 and arm64. Install it with the install script, Homebrew (macOS), scoop (Windows) or npm (npx @mcptask/cli); the mcptask-rails-runner gem is one more channel for Rails projects, not a requirement.
It is not just a 'dumb launcher' for the coding CLI. It adds orchestration, supervision, recovery, and quality control that a bare CLI does not have.
Pick the CLI that drives your project
The harness is the coding CLI the runner drives. Three profiles ship in the binary — Claude Code, Codex CLI and OpenCode — and a custom one lives either per machine in ~/.mcptask/harnesses/ or per project in config/harnesses/. The choice is made once per machine per project by init, which writes harness: into config/mcptask_runner.yml. That file is per-machine and git-ignored, so two developers on the same repository can drive different CLIs.
mcptask_runner init --cli codexThe three bundled profiles
Claude Code
mcptask_runner init --cli claude- Skills
- 12 in .claude/skills
- MCP config
- .mcp.json
- Instructions
- CLAUDE.md
- Models
- opus / sonnet / haiku aliases
- Approvals
- .claude/settings.local.json
Codex CLI
mcptask_runner init --cli codex- Skills
- 9 in .agents/skills
- MCP config
- .codex/config.toml
- Instructions
- AGENTS.md pointer
- Models
- gpt-6-astra, gpt-5.6-terra, gpt-5.6-luna
- Preflight
- codex login status must pass
OpenCode
mcptask_runner init --cli opencode- Skills
- 9 in .claude/skills
- MCP config
- opencode.json
- Instructions
- CLAUDE.md
- Models
- provider/model
- Approvals
- permission map injected by the runner
Logged in once, by you
The CLI has to be installed, and logged in where it needs a login, by whoever owns the machine. The runner never logs anything in — a credential it invented would authenticate as somebody — and each CLI has its own one-time login.
- Claude Code
Claude Code's own loginDeclares no probe — it needs none.- Codex CLI
codex logincodex login status has to exit 0 before a run starts; the preflight refuses the run rather than starting it.- OpenCode
no login for the default modelsOpenCode's default models are a free tier that needs no login. A host that logs in to another provider with opencode auth login must also name that provider's models under models: in config/mcptask_runner.yml.
Where the profile declares a probe, the runner checks it at start and refuses to run without it, so an unauthenticated CLI is refused by name rather than left to fail on its first call.
There is no default
A project that never named a CLI is refused by name, not quietly assumed to be Claude Code. launcher.command stays an override for the argv itself and is cross-checked against harness: — it cannot smuggle in a different CLI than the profile the project declared.
What is the same on all three
The profile holds the binary, the argv, the resume flag, the failure literals, the tool and skill names, and what init writes. Everything that makes the runner a runner is above it and does not change with the CLI: the dashboard card, the effort trail, the run log and the bug task look the same whichever harness runs.
The limits, stated plainly
Fork skills are Claude Code only
discover, memory-search and mcptask-read fork a cheap subagent so raw output never reaches the parent context. Only Claude Code can do that, so the Codex and OpenCode profiles ship nine skills instead of twelve. The nine helper scripts are the same on all three.
Codex needs a working login
codex login status has to pass before a run starts. The preflight refuses rather than spending a quota on a CLI that will fail on its first call.
OpenCode has no read-only flag
There is nothing to pass, so the runner injects a permission map instead. The read-only guarantee is the same; the mechanism is not.
Verified, not assumed
The conformance suite runs against all three dialects in CI, the chaos stress day runs in the local gate (bin/ci) before every merge, and the profiles are not a paper feature: a real Codex run finished a task on 2026-09-09, and OpenCode has real runs of its own behind it.
Pull requests on GitHub, Bitbucket or GitLab
The runner works with a repository on GitHub, on Bitbucket Cloud or on GitLab (gitlab.com and self-managed), and one command opens the pull request on any of them: mcptask_runner pr. No prompt and no bundled skill names a host CLI any more — the binary asks the host, so the same instructions work whichever host your repository lives on.
Nothing but mcptask_runner pr opens a PR
It is the fifth public subcommand. Every subcommand prints one JSON object on stdout, errors go to stderr with a non-zero exit, and --git-host overrides the host for a single invocation. git_host: itself is optional — the runner derives it from git remote get-url origin, and init --git-host bitbucket|gitlab is there for aliases, mirrors and self-managed instances.
Six subcommands
pr createOpen the pull request for the current branch.
pr listList pull requests, filtered by --task, --branch, --open or --state.
pr viewRead one pull request as JSON.
pr reviewsRead the reviews left on a pull request.
pr mergeMerge it — --squash and --delete-branch are the defaults, because that is what auto-squash means.
pr checksAsk the host what its checks say about the head commit.
Three hosts, three credential models
GitHub
Authenticated through gh and its own login. The pull-request template is read from .github/pull_request_template.md, which is a GitHub-only path.
Bitbucket Cloud
REST API 2.0, with one of two credentials and never both: a BITBUCKET_ACCESS_TOKEN for a machine (Bearer), or BITBUCKET_EMAIL plus BITBUCKET_API_TOKEN for a person (Basic). App passwords are refused — Atlassian ended them on 2026-06-09. init writes ~/.mcptask_env.d/bitbucket_credentials at mode 0600 (the permission check is skipped on Windows), never overwrites an existing value with nothing, and never prints the credential. Outside GitHub the template is declared as pr_template: path:.
GitLab
Driven by the glab CLI, which holds its own login — glab auth login, and the runner stores no GitLab credential of its own. gitlab.com and self-managed instances both work: a self-managed project declares git_host: gitlab (mcptask_runner init --git-host gitlab), because its origin URL is not gitlab.com, and the runner never takes a GitLab URL — glab already knows the instance it is logged in to. What pr create opens there is a merge request.
Auto-squash asks the host before it merges
A merge is not a hope that CI was green. The runner reads the host's checks for the head commit first: FAILED ends the run as ci_failed with the PR left open, IN_PROGRESS is retried ten times two minutes apart, and NONE — a repository with no host checks at all — falls back to the local gate: a green bin/ci lets the merge go ahead, and a project with no bin/ci ends as merge_skipped_no_ci, with the PR left open for a person to merge. GitLab reports one pipeline status per merge request, so the checks view there is a single row, not a list.
GitHub Enterprise is not github.com — unless you declare it
When the host is derived from the origin URL, it is matched by its exact hostname, so a GitHub Enterprise instance is rejected with a message that says so rather than being treated as github.com. Setting git_host: github explicitly skips that derivation, and the runner takes your word for it. A PR opened against the wrong host API is worse than a run that refuses to start.
Where the runner stops and mcptask.online begins
This is the runner working with a repository on Bitbucket Cloud — not a Bitbucket integration at the mcptask.online level. Pairing commits and pull requests back to pieces runs on webhooks, and those stay GitHub and GitLab.
Loop pulls tasks; goal finishes them
mcptask_runner pairs a work loop (it eats one task after another from the queue) with a goal (it finishes each task to a green PR, not just 'made some edits'). Pick the mode that matches how long you want it to run:
| Loop shape | Mode | Auto-squash vs manual | Required flag |
|---|---|---|---|
| Run one executor and stop | once | manual | — |
once_dry | manual | — | |
once_auto_squash | auto-squash | — | |
review | manual | — | |
| Iterate until a stop status | today | manual | — |
today_auto_squash | auto-squash | — | |
queue_manualaliases: queue | manual | — | |
queue_auto_squash | auto-squash | — | |
workflow | manual | — | |
reviews | manual | — | |
| Walk a story's subtasks | story_manualaliases: story | manual | --story-id |
story_auto_squash | auto-squash | --story-id | |
task_manualaliases: task | manual | --task-id | |
task_auto_squash | auto-squash | --task-id | |
| Do that all day, every day | daily | manual | — |
A few modes behave differently from the shape they sit in
- once_dry, review and reviews skip triage entirely. once_dry just shows you the next task and exits; the two review modes go straight to the pull requests that need a review.
- workflow has two phases: reviews first (a PR blocks a human), then today's tasks.
- daily does not stop at the end of a day — only a crash, a signal, a failed handover, a stop request, a refusal from the account or a refused bug report ends the loop. On the way into its overnight sleep it may also hand itself over to a newer binary already on disk (see self-update below); the loop that wakes up tomorrow is the same loop, just running newer code.
- queue_manual and queue_auto_squash have no time-of-day gate; they run as long as tasks keep arriving.
Full feature list and changelog
Every feature, what changed in each release, the complete CLI reference and the technical detail behind quota, logs and self-update — on a page of its own.
Technical FAQ
Short answers to questions this page raises but does not answer in place. Each answer points at the owning section rather than restating it.
Does the runner need Ruby, bundler or a Gemfile?
No. Project metadata comes from CLAUDE.md and config from config/mcptask_runner.yml — neither goes through Ruby. No prompt names a framework command either: a project that needs a build step before its browser suite declares it in its own CLAUDE.md.
Does it work on a non-Rails project?
Yes — any project under git, on GitHub, on Bitbucket Cloud or on GitLab (gitlab.com and self-managed), with one of the three supported coding CLIs installed and logged in on the host and a CLAUDE.md declaring account_code and project_relative_id. There is no default CLI: init --cli claude|codex|opencode chooses it, launcher.command only overrides the argv and is cross-checked against harness:.
Can two runners share one checkout?
No. The second runner refuses to start; lock.go (AcquireInstanceLock) enforces one runner per checkout.
What happens when the daily quota runs out mid-task?
The runner kills the child, the attempt ends with status quota_exceeded_mid_task and no retry (no bug task is filed), and the loop ends. Only the daily mode comes back on its own: it sleeps until the next day and starts fresh with triage. Every other mode resumes on its next scheduled run.
What does a crash leave behind, and who finds out?
A hard failure (status error, status crash or status anomaly — a guard of the runner's own that did not hold in a run which then carried on) files its own bug task with the run log attached and a fingerprint; the same failure is reported at most once every six hours. The reporter cannot file the bug that says its own token is missing — that one surfaces through the exit code and the log.
See What you can see while a run is in flight, When a hard failure happens, a bug task files itself
Does it update itself unattended?
No, deliberately. `update` touches only skills and helpers by default; `update --self` upgrades the binary in the user's terminal, not in a background loop.
Which OSes does it run on, and what does a green local test run not cover?
macOS (launchd), Linux (systemd user timer) and Windows (Task Scheduler) each have their own scheduled job. A green local test run covers unit and system tests on the host's Go version; it does not cover the three-OS × two-Go-version matrix or the conformance scenarios that gate a release.
Turn the machine on in the morning
One static binary, one install line, and the runner starts taking tasks from your queue.
Free 30-day trial. No credit card. Cancel any time.