Skip to main content

Claude status in the menu bar

Is Claude having a problem right now — and is it your model? The public status.claude.com health — overall severity, active incidents, and any degraded service — folded into the sessions menu-bar plugin (no separate icon). It is quiet by design: when everything is operational the menu bar shows nothing extra. When there's an active incident, a glyph appears in the title and a "Claude status" section appears in the dropdown.

⚪ 1 · 🟢 37% · ⚠ ← sessions · usage · status (⚠ = an active incident)
─────────────────
Claude status — Minor Service Outage
🟡 Elevated errors on Claude Opus 4.8
⚠ affects a model you're running: Opus 4.8 (1)
↳ monitoring · since 17:35 · impact minor
↳ Open incident →
🟠 Elevated errors on multiple models
⚠ multi-model incident — may affect: Opus 4.8
↳ investigating · since 17:50 · impact major
↳ Open incident →
🟡 Claude API (api.anthropic.com) — degraded performance
Updated 17:57 (2m ago)
↻ Refresh status
─────────────────
⚪ <your sessions…>

When all systems are operational the section and the title glyph are absent — the sessions plugin shows exactly as before.

The headline feature — does it hit my model?

status.claude.com has no per-model component — model-specific issues surface only as free-text incident titles ("Elevated errors on Claude Opus 4.7"). So each active incident is matched against the model each live session is currently running on (read from the session transcript), and the incident line is annotated:

  • ⚠ affects a model you're running: Opus 4.8 (1) — the incident title names a model family+version that a live session is on (the (1) is how many sessions). Case-insensitive, so "opus 4.7", "Opus 4.7", "Claude Opus 4.7" all match.
  • ⚠ multi-model incident — may affect: … — the title is broad ("multiple / many models"), so it may affect your running models. Worded as "may" because it's uncertain.
  • No annotation — the incident doesn't name your model (it's still listed; it's just not yours).

The annotation only ever adds emphasis — it never turns the global signal green. Because incident titles are free text, a differently-phrased incident could be missed by the matcher; if that ever happens the incident is still shown (the global ⚠/🔴 and the incident line), so a missed match can't produce false reassurance.

How it works — producer / consumer

Same split as Claude usage, so the menu bar never blocks on the network:

  • Producerscripts/status-fetch.sh, run every 5 minutes by a launchd LaunchAgent (io.bewith.status.plist). It GETs https://status.claude.com/api/v2/summary.json (overall indicator + components + active incidents in one request) and writes a small cache at ~/.bewith/cache/status.jsonatomically (temp + rename). No credential — the status API is public (unlike the usage feed, which needs the Keychain OAuth token).
  • Consumer — the sessions reader scripts/sessions-view.mjs imports scripts/status-view.mjs and renders the cache into the title glyph + dropdown section. It does no network.

Why poll summary.json every 5 min and not a webhook or the Slack app: a webhook needs a hosted endpoint (absurd for a local tool); the Slack app is org-level and push-into-Slack-then-poll-Slack. The menu bar is a polling display regardless, and incidents run for tens of minutes — so a few minutes of detection latency is immaterial. (If you ever want a team-wide shared alert, the native Claude Status → Slack app is the zero-code answer — a complementary layer, not a replacement.)

Why summary.json and not the history.atom RSS feed: the feed is incident history (resolved + active mixed, requiring HTML parsing to find current state) and lacks the overall indicator and per-component status. summary.json gives all three, already filtered to active, as clean JSON.

Install

It rides the sessions menu-bar installer — the same one command installs the sessions plugin, the usage feed, and the status feed, and kicks one immediate fetch:

pnpm sessions:menubar # = bash scripts/install-sessions-menubar.sh

The installer also registers SwiftBar as a Login Item so the icon survives a reboot (see Survives a reboot). Re-running is always safe (idempotent bootoutbootstrap).

bash scripts/install-sessions-menubar.sh --uninstall # removes the plugin + both feeds + caches + the Login Item

The states it shows

The title glyph mirrors the overall Statuspage indicator; the dropdown carries the detail.

IndicatorTitle glyphMeaning
none(nothing)All systems operational — the section is hidden.
minor· ⚠A minor incident is active.
major / critical· 🔴Major / critical impact.
maintenance· 🛠Active scheduled maintenance.

On a fetch error the feed stays quiet (no glyph, no section) rather than showing a false problem — the error is in ~/.bewith/cache/status.log. ↻ Refresh status in the dropdown forces a live fetch.

Caveats

  • Free-text matching. The per-model annotation matches against incident titles, which Anthropic writes as free text. The matcher handles the observed forms (Opus 4.8, Claude Opus 4.8, opus 4.7, multiple models); an unusual phrasing may not annotate. By design this only ever under-annotates — the incident is always still shown.
  • Broad incidents over-flag. "multiple / many models" annotates every running model as "may affect" — the safe (no-false-reassurance) side.
  • Public, but uncontracted. The Statuspage v2 JSON is stable and widely used, but it's still Anthropic's surface; if it changes, the feed shows nothing rather than a wrong state.

Verify it works

bash scripts/status-fetch.sh # producer: writes ~/.bewith/cache/status.json
cat ~/.bewith/cache/status.json | python3 -m json.tool # state:"fresh", data.status.indicator, data.incidents[]
node scripts/status-view.mjs # render the current cache (or "all clear")
node scripts/status-view.mjs --check-matcher # run the model-matcher over a corpus of real titles
node scripts/sessions-view.mjs --swiftbar # the folded menu (title glyph + "Claude status" section)
launchctl print "gui/$(id -u)/io.bewith.status" | grep -E "state|runs" # the agent is loaded

See also