AI & MCP

Testing an MCP server locally: six calls that settle the doubt

Test an MCP server with six calls: handshake, list, call, notification, unknown method and bad token. If they match the spec, a client can use it.

Key points

  • Read the handshake on three fields: protocolVersion, capabilities and serverInfo.name.
  • A notification, an unknown method and a bad token answer differently, so test all three.
  • The EneUI endpoint answers 200 even when the body holds a JSON-RPC error.

You test an MCP server with six calls: initialize, tools/list, tools/call, a notification, an unknown method and a bad token. If the answers match the specification, the server is ready for a client.

Start with the handshake

curl -s https://ui.eneawork.it/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize"}'

Check three fields: protocolVersion 2025-06-18, the capabilities object and serverInfo.name. If the server declares tools only, expect no prompts and no sampling: the empty list is consistent, not broken.

Then the list and one call

curl -s https://ui.eneawork.it/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

curl -s https://ui.eneawork.it/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_components","arguments":{"query":"data table","limit":2}}}'

The list must hold 9 tools with their input schema; the call returns text content with a URL and an install command. A query with no hits produces a message with isError: true, not a JSON-RPC error.

The answers nobody tests

ProbeExpectedOn the EneUI server
Notification without id202, empty body202, empty body
Unknown method-32601-32601
Invalid token401 and WWW-Authenticate401 with resource_metadata
Malformed JSONJSON-RPC error400 with an HTML body

The last row is the common defect: the Express parser answers before the dispatcher, so the error arrives as HTML and a client that reads JSON only treats it as a network fault.

Test the rate limit by counting

Anonymous traffic goes through a per-IP limit, RATE_MCP_PER_MIN, 120 per minute when the variable is unset; authenticated traffic has a per-plan quota. Past the limit you get 429 with Retry-After: 60 and code -32002.

for i in $(seq 1 3); do
  curl -s -o /dev/null -w "%{http_code}\n" https://ui.eneawork.it/mcp \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","id":1,"method":"ping"}'
done

A local run without writing a client

Start the server with npm start, export RATE_MCP_PER_MIN=5 and repeat the loop: the 429 must arrive on the sixth call. If it does not, the counter is per account or not mounted on the right path.

The EneUI catalogue

The probes use real records: modals, heroes and the Aceternity animated modal, MIT licence and 290 stars declared upstream, read on 1 November 2026.

The limitation

The server answers 200 even when the body holds a JSON-RPC error: anyone checking the HTTP status alone counts a failed call as a success. The test must read error and isError, not the status code.

Where to continue

The OAuth flow and the 401 are in OAuth for MCP servers; the methods are in how to build an MCP server; the tool schema is in designing an MCP tool. Resources against tools is in resources and tools in MCP.

Frequently asked questions

Is curl enough to test an MCP server?

For most of it, yes. Six calls cover the handshake, the list, a call, a notification, a method error and authentication, with no client installed.

Why does a notification return nothing?

Because a notification has no id and the protocol forbids answering it. The server closes with 202 and an empty body: a body here would be a bug.

How do I test the rate limit locally?

Set RATE_MCP_PER_MIN to a low value such as 5, restart the server and repeat the same call: the 429 with Retry-After must arrive on the sixth.

What does -32601 mean?

Method not found: the server received a method it does not implement. On EneUI that happens with prompts/get and sampling/createMessage.

Does a 200 mean the call succeeded?

No. A JSON-RPC error travels inside a 200 response; you must read the error field or isError in the tool result.