Home
Home
Blog
Blog
Work
Work

Quick Links

Work
Work
Blog
Blog
Home
Home

Stay Updated

Get notified about new articles, projects, and updates.

Tope Akinkuade | 2026

Back to work
Software EngineeringFeatured

Mobius MCP

Mobius is an open-source, local-first MCP server that gives AI coding agents live access to your web app's console logs, errors, and network requests, so you stop copy-pasting devtools into chat.

MCPTypescriptNode
View liveView code

On this page

Why MobiusHow it worksThe browser extensionHow to use itWant to break it with me?What's next

Say your app throws an error. You open devtools, read the red text, screenshot it, paste it into Agent, then type a sentence explaining what it's looking at and your expectations. Five minutes later, your error boundary catches another exception. Instead of breaking your momentum to manually trace the bug, you perform the exact same dance again and again until it works.

Your agent has read every file in your repo, traced a function through ten of them, and rewritten half of it without blinking. But the browser tab in the desktop right next to your editor might as well be on the moon. Every console.error, every failed request, every unplanned redirect, it's blind to all of it until you walk over, collect the evidence, and hand it across by hand.

So I built mobius-mcp to get myself out of the middle. It's a local MCP server that gives Claude Code, Codex CLI, Gemini CLI, or any MCP-speaking agent live access to your web app's runtime: console logs, errors, network requests, navigation events. It runs on your machine, no cloud or telemetry, no account to create.

Why Mobius

A Möbius strip is a loop with one continuous surface. Run your finger along it and you never cross an edge, and it feels endless. That's the shape of the thing I wanted, "continuity". The agent writes code, the code runs in the browser, the browser coughs up logs and errors and network noise, the agent reads that, adjusts, and round it goes. A loop.

Mobius exists to make the agent and the environment it's building for one continuous surface, so the loop actually closes and nobody has to ferry anything across the gap. The agent keeps testing, keeps working, keeps receiving what's really happening, while you sit there, watch and maybe interact with the exact same reality it's reacting to.

And the browser is only the first surface. Plenty of things an agent builds run somewhere where it might just be an extra hassle for runtime data and logs to get to the agent: an Electron renderer process, a React Native runtime on a device or simulator, other app shells whose console never surfaces on the generic developer's terminal.

How it works

A client sits inside whatever you're running, either a browser extension or an npm package, and captures runtime events as they fire. It streams them over a WebSocket to the local server. The server keeps a rolling in-memory history and exposes it to your agent as MCP tools: get_recent_logs, get_recent_errors, get_network_requests, and a growing list of others it can call whenever it needs the actual state instead of a guess.

Web App
  │
Extension OR npm package
  │
WebSocket
  │
localhost
  │
mobius-mcp
  │
MCP
  │
Claude Code / Codex / Gemini CLI

Everything in that diagram stays on your machine.

The repo is an npm workspaces monorepo. Early on I pulled the part that captures events, the code that patches console.*, hooks window.onerror, wraps fetch and XHR, and watches navigation, out into its own package: packages/capture-core. Neither the extension nor the npm client owns that logic. Both import it.

There's one reason, and the whole design leans on it. The server cannot tell the difference between events coming from the extension and events coming from the npm client, on purpose. They emit an identical, versioned protocol, basically a single shared core and two thin wrappers around it. The extension wrapper handles Chrome APIs and opt-in tab state. The npm wrapper handles startMobiusStream() and whatever your bundler decides to do that day. Neither one gets to reinvent what "capture a console.error" means.

It's also the reason the npm client isn't doomed to be a React-web-app-only thing forever. capture-core doesn't know or care whether it's running inside a content script or inside your app's own bundle.

The browser extension

The extension is a Chromium extension that captures runtime events without touching your app's code at all. It's live on the Chrome Web Store, so adding it is one click. From there, click the toolbar icon and hit "Enable tab" on whatever you're debugging. That click is the one opt-in it asks for.

For the dev servers frequently used, you don't want to keep clicking; there's a settings page where you can add a hostname/port rule, localhost:5173 for example, and matching tabs auto-enable on navigation. You can enable multiple tabs independently, and the server treats each as its own logical client, so two tabs running at once never tangle their histories together.

The extension also gets a set of powers the npm client structurally can't have, because they lean on chrome.debugger and the Chrome DevTools Protocol: screenshots, DOM and accessibility snapshots, CPU and memory profiling, running arbitrary JS in the tab through evaluate_js, full network response bodies. The protocol reports what a given client can actually do the moment it connects, so if an agent asks the npm client for a screenshot, it fails clearly instead of hanging there while you wonder what went wrong. One thing worth knowing up front: any CDP tool makes Chrome show a persistent "being debugged" banner on the tab once it's used. That's Chrome's own indicator, not something the extension can or should hide, and it attaches once per tab, not once per call.

How to use it

mobius-mcp is on npm. Register the server with your agent; for Claude Code:

claude mcp add mobius-mcp -- npx -y mobius-mcp

Or drop this into whatever JSON config your MCP client reads. Claude Code, Codex CLI, and Gemini CLI all take the same shape:

{
  "mcpServers": {
    "mobius-mcp": {
      "command": "npx",
      "args": ["-y", "mobius-mcp"]
    }
  }
}

Then stream your app's runtime into it. Preferably, you can install the browser extension from the Chrome Web Store and enable it on your tab. worst case, drop the npm client straight into your app:

npm install mobius-client
import { startMobiusStream } from "mobius-client";

startMobiusStream();

Unlike the extension, the npm client has no toggle. Capture starts the moment startMobiusStream() runs, because writing that import was already the opt-in.

After that, you just ask. Tell your agent to check the console, look for errors, see what that last request actually returned, and it calls the tools itself.

Right now that covers recent logs and errors, network requests, connected tabs, capture settings, debug sessions that hand back a time-ordered timeline instead of forcing the agent to correlate snapshots by hand, wait_for_* tools that block until something happens instead of polling in a loop, and, extension-only, the CDP-backed tools above.

If you'd rather build from source than use the published package:

npm install
npm run build
npm run start --workspace=apps/mcp-server

There's also a bundled agent skill in skill/ that tells the agent when to reach for these tools, I'm planning on adding more skills to tackle different scenarions efficiently.

Want to break it with me?

This is open source and I genuinely want other people's hands in it, not just their npm installs. If something breaks, a tool hangs when it shouldn't, or an event never shows up in get_recent_logs, open an issue on GitHub. Tell me what you were running, extension or npm client, which tool call misbehaved, and a minimal repro if you can manage one. That last part is worth more to me than anything else you could send.

For code contributions, CONTRIBUTING.md has the short version: fork it, npm install && npm run build && npm run test, keep the diff focused on one change, run npm run lint before opening the PR. The one rule I'd underline in red: if you touch capture logic, it has to stay identical across the extension and the npm client. The server's entire value depends on not being able to tell them apart. Open an issue before starting anything big so we don't both build the same thing twice.

What's next

It started life as a log bridge. The direction now is broader than that: a browser runtime service the agent can command, not just read from. Screenshots, DOM snapshots, CPU and memory profiling, and evaluate_js are already shipped. Roughly in the order I'd tackle what's left:

Framework introspection is probably first. React, Redux, and Zustand state, plus cookie, localStorage, and IndexedDB inspection. Today the agent sees what happened at the console and network layer. It still can't ask what the store actually looks like right now, which is the question you'd ask first as a human.

Event context needs to get richer. The events today are close to raw. A console.error gives you a message, not always a resolved, source-mapped stack, and repeated identical logs pile up as separate entries instead of one line collapsed with a count. Network events don't yet pair request and response detail on the same event, so the agent has to make a follow-up call it has to somehow know to make.

Durable log persistence, carefully. Both stores today are deliberately lossy: an in-memory ring buffer on the server, chrome.storage.session on the extension, both wiped on restart or reload. IndexedDB on the extension side and SQLite in WAL mode as a write-behind archive on the server are the plan. The retention window, and whether it's opt-in at all, are still open questions, because durable logs mean durable request bodies and headers sitting on a disk somewhere, and I'd rather answer that carefully than ship it fast.

The npm client, once it's off pause. It works at its current baseline today: console, error, and network capture through startMobiusStream(). I've held off on new work there because the framework and bundler quirks are a deeper hole than I wanted to fall into before the extension side matured. Vite and webpack HMR re-invoking the capture patch, Next.js SSR and RSC boundaries, React StrictMode's double-invoke. Once it catches up, capture-core is what turns Electron and React Native from a rewrite into an afternoon.

And multi-tab debug sessions. Sessions are single-tab only right now, and plenty of the bugs actually worth chasing live across more than one.

It's at version 1.0.0 now. A lot above still isn't built, but the foundation is set.

so if you're already running an agent in your terminal while building a web app, do the one thing that closes the loop: give it access to what you're actually looking at in the browser. npm install mobius-client, or load the extension and stop being the courier.