← Guides

Guide

How to Check if Your MCP Server Is Up

August 18, 2026 · 6 min read

"Is my MCP server up?" is not answered by opening the URL in a browser — most MCP endpoints return an error page or 405 to a plain GET even when they're perfectly healthy. The honest answer requires speaking the protocol: an initialize handshake and a tools/list call. Both are plain JSON-RPC over HTTP, so curl can do it. Here's the complete walkthrough for a Streamable HTTP server.

Step 1: the initialize request

Send a JSON-RPC initialize to your endpoint. The -i flag matters — you'll need a response header in step 2. The Accept header must offer both content types or compliant servers will refuse with 406:

curl -si -X POST https://your-server.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
    "protocolVersion":"2025-06-18",
    "capabilities":{},
    "clientInfo":{"name":"health-check","version":"1.0"}}}'

A healthy server responds 200 with either a JSON body or an SSE-framed message (event: message followed by a data: line — same JSON, different wrapper). What you want to see in it:

{"jsonrpc":"2.0","id":1,"result":{
  "protocolVersion":"2025-06-18",
  "capabilities":{"tools":{}},
  "serverInfo":{"name":"my-server","version":"1.4.2"}}}

Check three things: it's a result (not an error), the protocolVersion is one your clients can accept, and capabilities includes tools if you expect tools. Also look at the response headers for Mcp-Session-Id — if present, copy it; every following request must carry it.

Step 2: the initialized notification

Before making requests, a client is supposed to confirm the handshake. Notifications have no id and expect no body back — a 202 Accepted is the normal response:

curl -si -X POST https://your-server.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-06-18' \
  -H 'Mcp-Session-Id: PASTE-YOUR-SESSION-ID' \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'

Omit the Mcp-Session-Id line if step 1 didn't return one. Lenient servers work without this step; strict ones reject tools/list until they've seen it, so a thorough check includes it.

Step 3: list the tools

curl -si -X POST https://your-server.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-06-18' \
  -H 'Mcp-Session-Id: PASTE-YOUR-SESSION-ID' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

The result contains a tools array with each tool's name, description, and inputSchema. This is the step that separates "the process is running" from "the server is actually serving": count the tools and eyeball the names. An empty array on a server that should have tools is an outage in every way that matters, whatever the status code says.

Reading the failures

  • 406 — your Accept header doesn't offer both content types.
  • 401 / 403 — auth. Add your Authorization (or custom) header to every request above.
  • 400 on step 2 or 3 — usually a missing Mcp-Session-Id or MCP-Protocol-Version header.
  • 404 — wrong path, or an expired session id; re-run step 1.
  • 405 on POST — possibly a legacy HTTP+SSE server that wants a GET to /sse first; see the transport guide.
  • A hang with no response — almost always a buffering proxy or a cold start; the initialize failure guide covers both.

Checking a legacy HTTP+SSE server

If the POST in step 1 comes back 404 or 405, you may be talking to a server on the old transport. Those expect you to open the SSE stream first:

curl -sN https://your-server.com/sse -H 'Accept: text/event-stream'

A healthy legacy server immediately emits an endpoint event whose data: line is a URL, usually containing a session token. Keep that curl running in one terminal, POST the same initialize body from step 1 to the endpoint URL in another, and watch the response arrive on the stream rather than in the POST reply — the POST itself just returns 202. If the stream opens but no endpoint event ever arrives, a buffering proxy is holding it back. The -N flag disables curl's own buffering so you can tell the difference.

Making it repeatable

Once the three requests work, wrap them in a script and pipe the results through jq so a human doesn't have to read raw JSON-RPC:

curl -s ... -d "$TOOLS_LIST_BODY" \
  | sed -n 's/^data: //p' \
  | jq -r '.result.tools[].name'

The sed line strips SSE framing when present and passes plain JSON through untouched, so the same pipeline handles both response styles. A one-line cron job comparing that output against yesterday's copy is already a primitive drift detector.

From one check to continuous checks

This walkthrough proves your server is up right now. The failures that hurt are the ones that happen at 3am after a deploy, and the ones a handshake alone can't see — a tool list that quietly shrank, a tool whose upstream dependency died. That's a monitoring problem: the same sequence, every minute, with the tool list diffed between runs and an alert when anything changes. The complete monitoring guide covers the full setup — or paste your URL into the free checker below and get this entire walkthrough run for you in about two seconds.