Your platform team runs an internal "MCP marketplace" that lists every server the company runs: 500 of them, with their tools and whether they support Tasks.
In the legacy world, the nightly crawler had to initialize each server, call three different list methods, and tear the session down. Some servers rate-limited it; others kept zombie sessions around.
Now it's one server/discover call per server. The answer is marked cacheScope: "public" with a one-hour TTL, so the company gateway serves repeat lookups from cache. The marketplace shows a green "2026-07-28" badge for servers whose supportedVersions include it, and flags the ones that still answer with a legacy error.
server/discover takes no parameters beyond the standard _meta. It returns supportedVersions, capabilities (including any extensions), optional instructions for the model, and serverInfo in _meta. The result is cacheable, so it also carries ttlMs and cacheScope.
Servers MUST implement it. Calling it is optional for clients: a client may call any method directly and handle -32022 if the version is wrong.
Its second job is as a probe on stdio. A dual-era client SHOULD send server/discover first. A result, or a recognised modern error, means a modern server. Any other error, or a timeout, means a legacy server: fall back to initialize. Cache that answer for the lifetime of the server process.
Everything InitializeResult used to carry needed a new home. Putting it in a cacheable method means one call can show a server's identity and abilities, without probing tools/list, prompts/list and resources/list separately, and without opening anything that looks like a session.
What it used to look like (legacy, for comparison only)
Legacybefore
// legacy: identity and abilities came back from initialize{"jsonrpc":"2.0", "id":1, "result": {"protocolVersion":"2025-11-25","capabilities": { "tools": {}, "resources": {} },"serverInfo": { "name":"ExampleServer", "version":"1.0.0" },"instructions":"This server provides weather and resource utilities."}}
Verbatim from the spec. An extension such as Tasks is advertised here, under capabilities.extensions.
First time here? Set up the test helper (once per terminal)
ShellSetup
# 1. In a SECOND terminal, start the reference server (Node 18+, no dependencies)curl -sO https://www.diegozuluaga.dev/mcpa/reference-server.mjsnode reference-server.mjs # http://localhost:3000/mcp, logs appear here# 2. In THIS terminal, define the helper every test uses (bash or zsh)export MCP=http://localhost:3000/mcpMETA='"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}'mcp() { # usage: mcp <method> '<json body>' [extra curl args...] curl -sS -N "$MCP" \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H 'MCP-Protocol-Version: 2026-07-28' \ -H "Authorization: Bearer ${MCP_USER:-alice}" \ -H "Mcp-Method: $1" "${@:3}" -d "$2" \ -w '\nHTTP %{http_code}\n'}# Demo auth: the reference server treats the bearer token as the user's name.# Prefix a command with MCP_USER=bob to act as someone else.
ShellTests
# HTTPmcp server/discover '{"jsonrpc":"2.0","id":"d1","method":"server/discover","params":{'"$META"'}}'# expect: supportedVersions, capabilities (with extensions), serverInfo in _meta, ttlMs and cacheScope# stdio: the same server, speaking newline-delimited JSON on stdin/stdout{ printf '%s\n' '{"jsonrpc":"2.0","id":"d1","method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'; sleep 1; } | node reference-server.mjs --stdio# the sleep keeps stdin open long enough for the reply# a legacy server would answer "method not found" (or nothing): that is the fallback signal