Running a Public Status Page for Your MCP Server

If you operate an MCP server that other teams' agents connect to, you have a support problem a REST API operator doesn't: when your server misbehaves, the developer on the other end is debugging an agent, not reading your logs. Their symptom is "the agent stopped calling the tool" or "the agent hallucinated an argument that used to work" — not "GET /mcp returned 500." Without a place to check first, they burn an hour bisecting their own prompt, their own client code, and their own model version before they even consider that the problem is on your side. A status page is how you take that hour away from them.

A generic uptime status page — the kind built for a REST API or a marketing site — is necessary but not sufficient here. It tells a visitor "reachable" or "not reachable." An MCP server can be reachable and still be broken for every agent trying to use it: the handshake can fail while the endpoint returns 200, tools/list can throw while the base URL is fine, a tool's input schema can change shape overnight with no HTTP-visible signal at all. If your status page only reports HTTP reachability, it will read "operational" during an outage your own customers are actively hitting.


Why "reachable" isn't "working" for MCP

This is the same gap covered in how to health-check a remote MCP server: a correct Streamable HTTP server that only does request/response is allowed by spec to 405 a bare GET, and a CDN error page can return a clean 200. Neither of those facts is visible to a monitor that just checks the URL responds. The same gap shows up on the communication side. When something does go wrong, "MCP server experiencing issues" tells an agent developer nothing they can act on. Compare that to "the initialize handshake is failing — new sessions can't start, but active sessions are unaffected" or "the search_docs tool's input schema changed at 14:02 UTC, which will break any client still sending the old argument shape." The second kind of message ends the developer's debugging session immediately. The first kind extends it.

What actually needs to be visible, specifically because it's MCP and not a web page:

  • Handshake state. Did the JSON-RPC initialize request succeed? A server can be network-reachable and still refuse or error every session.
  • Transport. Streamable HTTP vs the legacy HTTP+SSE transport — see Streamable HTTP vs SSE for what each looks like on the wire. A transport change between two points in time is itself worth disclosing, because clients pinned to the old behavior can break the moment you migrate, with every ordinary HTTP signal staying green.
  • Tool inventory and drift. Which tools are currently advertised, and did a tool disappear, appear, or have its schema change since the last check? A removed or reshaped tool breaks any agent already calling it — silently, because nothing in the HTTP layer looks unhealthy.
  • Auth-gated vs down. A 401/403 means the server is up and simply requires a credential. Folding that into the same "down" bucket as a real outage trains your audience to ignore your status page.
  • Handshake latency. A slow initialize doesn't fail a request, but every agent session against your server will feel sluggish to start. That's worth a "degraded," not silence.

What Merlonix actually gives you here

It's worth being precise about which parts of this are MCP-specific and which are ordinary status-page infrastructure, because they live in different parts of the product.

The generic part is Merlonix's status-page product: create a page for a client, add components (each backed by a monitored asset), publish it at status.merlonix.com/<your-slug> (or a white-labeled custom domain on Agency+), post incidents with a status timeline (investigatingidentifiedmonitoringresolved) and impact level, run scheduled maintenance windows, and email confirmed subscribers on each incident update. None of that is MCP-aware — it's the same status-page machinery you'd use for a database or a checkout flow, defined in apps/api/src/routes/status-pages.ts.

The MCP-specific part is the mcp_health monitored check (apps/api/src/lib/mcpHealth.ts, wired into the check registry as mcpHealthDescriptor). Enable it on an asset and Merlonix runs the real JSON-RPC initialize handshake — not a GET — on a schedule, and records: handshake success (handshake_ok), the transport it detected (streamable_http vs sse), which spec generation the server answered on (the classic stateful handshake vs the 2026-07-28 stateless core — see why MCP initialize fails for what a rejected handshake on a migrated server looks like and why it isn't necessarily down), the full tool inventory with a per-tool contract digest for drift detection, whether the endpoint is auth-gated, the handshake round-trip latency, and an A–F security-posture grade covering tool-description poisoning signals, auth requirement, and transport security. It alerts on four distinct transitions: the server going down, recovering, a tool or capability drifting (with a breaking flag when a tool was removed or its schema changed vs merely gaining a capability), and a published /.well-known/mcp.json server card disagreeing with what the live server actually serves. This is the same probe behind the free MCP health checker, run continuously with drift memory instead of as a one-shot check.

How the two connect: a status-page component's public badge (operational / degraded / down) reads the availability probes on the asset behind it — the uptime check, heartbeat, port, and mcp_health — and takes the worst verdict. So a failing initialize handshake flips the tile to "down" on its own, and an mcp_health result of degraded (a server that answers, slowly or incompletely) shows as degraded rather than being rounded to either extreme. Certificate and DNS problems still down a component independently, because a host that answers a handshake while serving an expired certificate is not healthy either.

Worth saying plainly: the badge is not the incident. An automated tile can tell your audience that something is wrong; it cannot tell them what, and for an MCP server the "what" is the entire useful content — a handshake failure, a timing-out tools/list, and a changed tool schema are three different incidents with three different workarounds. Treat the badge as the thing that stops people from wondering, and the incident post as the thing that ends their debugging session. That is what the next section is about.


Setting one up

  1. Add your MCP endpoint's hostname as a monitored asset, and turn on mcp_health_enabled. This is the check that actually speaks JSON-RPC to your server instead of just GETing it.
  2. Create a status page for the client that owns it, and add the asset as a component. Give the component a label an agent developer would recognize — "MCP API" or the server name from your serverInfo, not an internal hostname.
  3. Publish the page (is_public: true) once you're ready for it to be linked from your docs, your README, and — this matters more for MCP than for a typical API — your /.well-known/mcp.json server card, so an agent or client doing connectionless discovery can find it in the same place it finds your endpoint.
  4. Watch the alerts, not just the page. The mcp_health down/recovery/drift/manifest alerts are your early warning; the public status page is where you tell your audience about it once you've looked.

What to say during an incident

The five things worth naming, in the order an agent developer needs them:

  • What's actually broken. "The initialize handshake is failing" is a different incident from "tools/list is timing out" is a different incident from "the search_docs tool's schema changed." Say which one. If you know it from the mcp_health finding — a JSON-RPC error code, a specific tool name, a latency figure — put it in the update verbatim; that's the sentence that ends someone's debugging session.
  • Whether it's total or partial. Down for new sessions but fine for existing ones (a handshake-only failure) is a materially different blast radius than down for every call.
  • Whether it's your server or a dependency. If a tool call fails because an upstream you depend on is down, say so — the agent developer's next move (retry vs. give up vs. work around it) depends on knowing whose outage this is.
  • What changed, if anything shipped. A tool drift incident should say exactly which tool, what changed about it (removed, renamed, schema reshaped), and whether it's a breaking change for clients still on the old contract — that's the entire point of the drift signal.
  • Resolution and what to expect going forward. Confirm the handshake and tool inventory are back to the previous contract, not just that the endpoint responds again — a server that "recovers" with a silently different tool schema hasn't actually resolved the thing that broke agents.

A generic "investigating connectivity issues" update is worse than saying nothing, because it burns the reader's trust the first time they discover — from your logs, or from their own agent's error — that the real problem was narrower or different than what you posted. Precision is the entire value a status page adds over silence; for an MCP server, that precision has to be expressed in MCP's own vocabulary — handshake, transport, tool contract — not HTTP's.

If you're setting up MCP monitoring for the first time, the MCP server developers page collects the rest of the toolchain — health checks, the manifest/server-card checker, and the security-posture scan — in one place, and the MCP directory is worth a look at how other live servers present themselves publicly.