I burned 20% of a weekly Grok Bot allowance in thirty minutes. Seven or eight bots running in parallel, all doing real work, all metering hard. The product is genuinely good; that was the frustrating part. The bottleneck wasn't capability. It was inference budget.
I already pay for Claude and for ChatGPT. Those subscriptions have their own limits, and they were sitting completely idle while Grok Bot ground through its quota.
So I built Locum, a provider adapter that lets Grok Bot delegate coding work to the agent CLIs already authenticated on my own machine. A locum is a qualified professional who temporarily does someone else's job, which is exactly the arrangement.
This post is about how it works, and more importantly about the one architectural decision that separates it from the version of this idea that gets you banned.
The constraint that decides everything
Grok Bot doesn't run on your laptop. Each bot gets a persistent computer in xAI's cloud, which is what makes it useful: it keeps working after you close the lid. It also means the bot cannot see localhost. There is no local plugin surface to hook into.
What it does have is documented: custom MCP connectors. You point it at a public HTTPS endpoint speaking the Model Context Protocol, it discovers your tools, and it can call them. xAI even publishes a guide for tunnelling a server running on your own machine.
That single fact fixes the shape of the whole thing. Locum has to be a remote MCP server that reaches back to your machine, not a patched binary and not an intercepted protocol.
The line I would not cross
There are two ways to build "use your Claude subscription somewhere else," and they are not close to each other.
The tempting one: read the OAuth token that Claude Code stores, and call Anthropic's API yourself with it. It works. It is also explicitly prohibited. Anthropic's own Claude Code legal page says OAuth is "intended exclusively" for ordinary use of Claude Code and other native Anthropic applications, and that they do not permit third-party developers to route requests through Free, Pro, or Max plan credentials on behalf of their users. People have shipped tools on the other side of that line and watched them get shut off.
The other way: don't handle credentials at all. Spawn the official binary.
The credential boundary. Locum is a launcher, not a proxy.
Locum runs claude -p "<prompt>" as a subprocess. That's it. The CLI you already logged into does the authenticating, exactly as it does when you type the command yourself. Locum never opens ~/.claude, never touches the keychain, never sees a token, and never makes a request to api.anthropic.com.
Four invariants are written at the top of the source file, and they're the reason the project is publishable:
- Never read credential files, keychains, or OAuth tokens.
- Never call
api.anthropic.comorapi.openai.comdirectly. - Only ever spawn the official
claude/codexbinaries. - Single-operator. One token, one allowlist of workspace roots.
One more thing it deliberately does not do: bypass Grok Bot's own entitlement. You still need Grok Bot access for a connector to exist at all. Locum changes where the inference comes from. It does not get you Grok Bot for free, and I'd have no interest in shipping the version that did.
The shape of it
Five tools cross that boundary: delegate_to_claude, resume_claude, delegate_to_codex, check_job, cancel_job.
About 640 lines of Python, one file, no framework beyond an MCP server library.
Async is the entire trick
This is the part that decides whether the integration works at all, and it's easy to get wrong.
MCP tool calls are request/response with a timeout measured in tens of seconds. Real coding tasks take minutes. If delegate_to_claude tried to run the task and return the answer, every useful call would time out, and worse, it would time out after burning the work, with the result stranded in a dead subprocess.
Build this in from the start. Retrofitting it means rewriting the server.
So delegate_to_claude starts the job and returns a handle immediately. The bot polls check_job until the status changes.
The polling is more useful than it sounds. Locum runs the CLI with --output-format stream-json and parses the event stream as it arrives, so a poll returns not just "still running" but the turn count and the last few tool calls:
{
"job_id": "dacb5836aa66",
"status": "running",
"elapsed_seconds": 74.2,
"turns": 9,
"recent_activity": ["Read src/auth.ts", "Edit src/auth.ts", "Bash npm test"]
}
The bot can narrate real progress instead of staring at a black box. When the job finishes, the result comes back along with a session_id, which matters, because resume_claude reuses it. A cold delegation re-pays about 18k tokens of CLAUDE.md and system prompt setup; resuming hits the prompt cache instead. Follow-ups should always resume.
Grok speaks OAuth, and only OAuth
I built the first version with a bearer token. Simple, adequate for a single-operator tool, easy to test with curl.
Then I opened Grok's custom connector dialog and found no field for a header. It had probed my server, got a bare 401, and fallen back to asking me for OAuth app credentials: client ID, authorization endpoint, token endpoint, PKCE method.
That's the MCP specification's authorization flow, and it isn't optional. A remote MCP server that wants a real client has to be an OAuth 2.1 authorization server.
So Locum became one. It's less code than it sounds:
Discovery, dynamic registration, PKCE, refresh. No third-party OAuth app to create.
The consent screen is the load-bearing part, and it's worth being explicit about why. /authorize sits on a public tunnel. Without a gate, anyone who learned the URL could walk through the flow, mint themselves a token, and get shell access to my machine. So the consent page demands a passphrase and shows the redirect target before you approve. If that destination isn't the one you expect, you're looking at someone else's authorization attempt.
Codes are single-use and expire in 120 seconds. S256 is required; plain is refused. Every comparison is constant-time. There's a test file that exercises all of it: discovery, registration, the consent gate, PKCE enforcement, replay rejection, refresh. Security code that isn't tested is decoration.
Three bugs worth your time
The architecture took an afternoon. These took longer.
The 404 that was a port collision
Everything started returning 404. Every path, including /health, which is an unconditional route. The tunnel reported healthy, ingress validated, the routing rule matched.
The response was content-length: 10. Exactly the length of Not found., and that string wasn't in my server.
The Grok Bot app itself listens on port 8787. macOS prefers ::1, so the tunnel talked to it instead.
The Grok Bot desktop app listens on [::1]:8787. Locum was on 127.0.0.1:8787. Same port, different address families, so there was no "address already in use" error, no warning, nothing. macOS resolves localhost to ::1 before 127.0.0.1, so every tunnelled request reached the wrong process.
Two things found it. lsof -nP -iTCP:8787 -sTCP:LISTEN showed both listeners, one line each. And cloudflared --loglevel debug logs ingressRule= and originService= per request, which separates "cloudflared's catch-all matched" from "the origin returned this". I'd assumed the former and was wrong.
Two fixes, because either alone leaves a trap: move to port 8791, and target 127.0.0.1 explicitly rather than localhost.
The service daemon that installed itself broken
sudo cloudflared service install writes a launch daemon whose ProgramArguments is just the binary path. No tunnel run. No --config. It runs as root, whose $HOME is /var/root, so ~/.cloudflared/config.yml is invisible to it, and it crash-loops on a 5-second KeepAlive.
The failure hides itself perfectly: the tunnel keeps working, because your user-level cloudflared tunnel run is still serving. You end up with a crash-looping root daemon and a working user process registered as two connectors for one tunnel. Adding a connector while that's in flux is how you get "Connection failed" from a server that answers every external probe.
The fix is to put the config where root can read it, /etc/cloudflared, and rewrite credentials-file to match.
The nested-session auth failure
Every delegated job died with Failed to authenticate: OAuth session expired and could not be refreshed.
Claude Code exports session plumbing into every child process it spawns: CLAUDECODE, a family of CLAUDE_CODE_* variables, and ANTHROPIC_BASE_URL. A claude that inherits those believes it's a nested child session and tries to delegate authentication to a host socket that isn't listening.
Locum now strips them before spawning, but only when it detects it's nested, so a deliberately-set ANTHROPIC_BASE_URL still works in a normal terminal.
The lesson generalises past this project: when you spawn a CLI from a server, you inherit an environment you did not design. Decide what crosses that boundary.
What it actually saves
Honesty matters more than the pitch here, so:
It reduces Grok Bot usage. It does not eliminate it. The bot's own orchestration turns still meter. The win is collapsing fifty bot steps into one tool call and a few polls, moving the long agentic grind onto the subscription you already pay for.
Your machine has to be awake with the tunnel up. It's a personal tool, not a service.
Those subscriptions have limits too. This changes where the inference budget comes from. Nobody found an infinite-token glitch.
The skill file matters more than the tools. Without an instruction telling the bot to prefer delegating, it has the tools available and keeps grinding through its own loop anyway. That one markdown file is the difference between the integration working and merely existing.
Running it
It's open source. You need claude or codex already signed in, a domain on Cloudflare for a stable hostname, and Grok Bot access.
git clone https://github.com/HarjjotSinghh/locum
cd locum && cp .env.example .env # set a token and your workspace roots
set -a && source .env && set +a
uv run server.py
Then a tunnel, a custom connector pointed at https://your-host/mcp, and the skill file pasted into a Grok Bot Skill.
The invariants at the top of server.py aren't decoration. If a change would break one of them, it's the wrong change, and that is what keeps this a tool worth using rather than a liability worth avoiding.
Not affiliated with or endorsed by xAI, Anysphere, OpenAI, or Anthropic.