Most people monitor an MCP (Model Context Protocol) server the same way they'd monitor a REST API: ping an endpoint, check for a 200, call it a day. The problem is that an MCP server isn't a REST API. It's a JSON-RPC-based contract between a server and an AI client, and the things that actually break it — a malformed tools/list response, a tool schema that silently changed shape, a handshake that times out under load — are invisible to a plain uptime check.
Here's what monitoring an MCP server actually requires, and where most setups fall short.
Start with the handshake, not the port
Every MCP session begins with a specific sequence: the client sends initialize, the server responds with its protocol version and capabilities, the client sends notifications/initialized, and only then does real work begin — typically a tools/list call to discover what the server can do.
A server can be "up" at the TCP or HTTP level and still fail this sequence. It might return a capabilities object with a version mismatch, hang on the initialize response, or time out before completing the round trip. None of that shows up in a plain uptime ping. If your monitoring doesn't actually speak the protocol — sending a real initialize and validating the response — you're monitoring the process listening on the port, not the MCP server itself.
Treat tools/list as the real signal
Once the handshake completes, tools/list is where the actual contract lives: the names, descriptions, and JSON Schema definitions of every tool the server exposes. This is what an AI client reads to decide what it can call and how.
A healthy monitoring setup should:
- Call
tools/liston a schedule, not just at deploy time. - Compare the response against a stored baseline.
- Flag any change to a tool's name, required parameters, parameter types, or enum values.
This matters because MCP has no built-in versioning enforcement for tools. A server can rename a parameter, tighten a type, or drop a tool entirely, and nothing in the protocol stops it or announces it. The only way to catch it is to keep watching.
Separate "down" from "different"
Most monitoring tools collapse everything into a single up/down state. MCP servers need at least two failure categories:
- Availability failures — the handshake doesn't complete, requests time out, or the transport (stdio, SSE, or streamable HTTP) drops connections.
- Contract failures — the server responds fine, but what it returns has changed in a way that could break clients: a tool schema drifted, a description changed enough to alter model behavior, or an error code that used to be a success.
The second category is arguably more dangerous, because everything looks fine from a traditional monitoring dashboard. Requests succeed, latency is normal, and yet an agent calling that tool starts failing validation or passing malformed arguments because the schema underneath it moved.
Check latency and error codes, not just success/failure
MCP runs over JSON-RPC, so a "successful" HTTP response can still carry a JSON-RPC error object in the body. Monitoring that only checks the transport status code will miss this. A proper check parses the JSON-RPC envelope and confirms:
- The response is a valid JSON-RPC 2.0 message.
- There's no
errorfield, or the error is one you expect and handle. - The result matches the expected shape for that method.
Latency matters too, but with different thresholds per method — initialize and tools/list are typically infrequent and can tolerate more variance, while individual tools/call invocations are often on a user-facing critical path.
Build (or use) a baseline diffing loop
In practice, the monitoring loop that works looks like this:
- Run the full handshake against the server on an interval.
- Call
tools/listand hash or diff the result against the last known-good version. - Alert on availability failures immediately.
- Alert on schema drift separately, with enough context (what changed, old vs. new) to triage quickly.
- Expose the current status somewhere your team — or your users — can see it.
That last point is easy to skip but valuable: a public status badge for an MCP server tells integrators, at a glance, whether the server is healthy and whether its tool contract is stable, without them needing to run their own checks.
This is the exact gap MCP Vitals is built to close — it runs the real initialize → tools/list handshake against your server on a schedule, diffs the tool schemas for drift, and gives you a badge and alerts instead of a plain uptime ping.
A quick worth-watching note
The MCP specification is still evolving quickly, and proposals are already circulating for a more stateless core transport in future revisions. Whatever transport details change, the underlying monitoring principle won't: you need to verify the protocol contract itself, not just that something answered the door.
Closing
Monitoring an MCP server "the right way" isn't about fancier dashboards — it's about checking the things that actually break: the handshake, the tool contract, and the shape of the responses your clients depend on. Uptime is necessary but nowhere near sufficient. If you're not already diffing your tools/list output over time, that's the first gap worth closing.