skip to content

In MCP, what does server/discover return, and which side is required to implement it?

level: juniorimportance: must knowfreq 68%

answer

  1. one call, whole server profile
  2. servers must, clients may
  3. no arguments, only _meta
  4. versions, capabilities, instructions, cache hints
  5. not a handshake, nothing is negotiated

basics

~10 s

server/discover returns a DiscoverResult holding supportedVersions, the server's capabilities, optional instructions, and the required cache hints ttlMs and cacheScope, with serverInfo in _meta. Servers MUST implement it; clients MAY call it.

solid answer

~40 s

In MCP revision 2026-07-28 there is no `initialize` handshake, so `server/discover` is the one call that describes a server in a single round-trip. The request is unusual in that its `params` carry only `_meta` — no arguments — and the `_meta` is the same per-request block every MCP request sends. The response, `DiscoverResult`, carries `resultType: "complete"`, a `supportedVersions` array of date-string revisions, a `capabilities` object, an optional free-form `instructions` string, and — because it extends `CacheableResult` — the required `ttlMs` and `cacheScope` fields; `io.modelcontextprotocol/serverInfo` SHOULD appear in the result's `_meta`. The obligation is deliberately asymmetric: every conformant server MUST implement the method, while a client MAY skip it entirely and go straight to `tools/list`, because each request already declares its own version and capabilities.

code

json · 11 lines
json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "server/discover",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

go deeper

for a junior

Remember the two headline facts: server/discover reports supported versions, capabilities and optional instructions in one call, and servers must implement it while clients may skip it.

for a middle

Be able to name the DiscoverResult fields and say where the request's version and capabilities live. Explain that params carry only _meta and that resultType is always complete for discovery.

for a senior

Show that discovery is an optimisation, not a gate: because each request is self-contained under revision 2026-07-28, a client may skip it, and a server must not treat a prior discover call as context for later requests.

for a principal

Own the platform view — what a host caches from discovery, how it presents instructions and untrusted serverInfo to users, and why the mandatory-server/optional-client split keeps clients cheap while keeping compatibility checks possible.

## Why discovery exists at all Before revision 2026-07-28, an MCP client learned what a server could do by opening a connection, sending `initialize`, reading the `InitializeResult`, and then sending `notifications/initialized`. That handshake was removed in 2026-07-28. MCP is now a stateless protocol: every request is self-contained and carries its own protocol version and capabilities in `params._meta`, and a server MUST NOT rely on prior requests over the same connection to establish context. That removal left one genuine gap. A client that has never spoken to a server still wants a cheap way to ask "which revisions do you speak, what do you offer, and is there anything I should tell the model about you?" `server/discover` is exactly that question, answered in one round-trip. ## The obligation is asymmetric Servers **MUST** implement `server/discover`. Clients **MAY** call it. That asymmetry is the single most commonly missed fact about the method. It follows directly from statelessness: because every request already declares `io.modelcontextprotocol/protocolVersion` and `io.modelcontextprotocol/clientCapabilities`, a client loses nothing structural by never discovering — it can send `tools/list` as its first message. But any client that *does* want to look before it leaps must be able to assume the method is there, on every conformant server, which is why the server side is a MUST. ## The request shape `DiscoverRequest` params carry **only** `_meta`. There are no arguments, no cursor, no filter — nothing to pass. The `_meta` object is the ordinary `RequestMetaObject`: the required `io.modelcontextprotocol/protocolVersion`, the required `io.modelcontextprotocol/clientCapabilities` (an empty object `{}` is legal), and the optional `io.modelcontextprotocol/clientInfo`. ## The result shape `DiscoverResult` contains: - **`resultType`** — `"complete"`. Every MCP result carries a required `resultType`; discovery is always a complete result, never the `"input_required"` interim form. - **`supportedVersions: string[]`** — the revision date-strings this server accepts. - **`capabilities`** — the server capability object, telling the client which feature areas the server offers. - **`instructions?`** — optional free-form text describing how to use this server. It moved here from the old `InitializeResult`. - **`ttlMs` and `cacheScope`** — required, inherited from `CacheableResult`. `ttlMs` says how long the client may cache this answer; `cacheScope` is `"public"` or `"private"` and says whether the cached copy may be shared across users or must be kept per-authorization. - **`_meta`** — SHOULD carry `io.modelcontextprotocol/serverInfo`, the server's self-reported name and version. `instructions` and `serverInfo` are the two fields that visibly migrated out of the removed handshake. ## What it is not It is not a handshake, and it does not negotiate anything. There is no agreement step in MCP 2026-07-28: the server simply accepts or rejects each request on its own merits, and `supportedVersions` is informational — the client reads it and decides what to send next. Calling `server/discover` also does not register the client or open anything. An open connection, such as a stdio subprocess, is not a conversation or a session, so a discover call earlier on the same pipe grants a later `tools/call` nothing. If a server needs the client to prove something, that must travel on the request that needs it. ## Trust `serverInfo` — like `clientInfo` — is self-reported and explicitly untrusted. Show it in a UI, log it, use it for telemetry; do not make an authorization or identity decision from it, and do not assume the `name` is globally unique across the servers a host has connected. ## In practice A typical host calls `server/discover` once when a server is first configured, renders the name, version and instructions to the user, caches the answer for `ttlMs` under the scope `cacheScope` names, and thereafter goes straight to the primitive calls it needs. A minimal script client may never call it at all — and that is a conformant client.

  • If a client never calls server/discover, does the server behave any differently?
    No. Every MCP request in revision 2026-07-28 carries its own protocol version and client capabilities in `params._meta`, and the server MUST NOT infer anything from earlier requests on the same connection. A first message of `tools/list` is just as valid as one preceded by discovery; the server answers it on its own merits.
  • Why does DiscoverRequest have no parameters of its own?
    There is nothing to ask for. The result is a fixed profile of the whole server — supported versions, capabilities, instructions, cache hints — not a queryable slice of it, and there is no state to open. Everything the server needs about the caller already arrives in the required `_meta` block, so the params object holds only `_meta`.
  • Can a client trust the name and version in serverInfo?
    No. `io.modelcontextprotocol/serverInfo` in the result's `_meta` is self-reported and explicitly untrusted, exactly like `clientInfo` on requests. Display it, log it, use it for diagnostics — but never base an authorization decision on it, and never assume the name uniquely identifies a server among the several a host may have connected.

Think of it as a shop's window sign rather than a doorman: any shop has to put one up, but a customer who already knows what they want can walk straight in without reading it.

saying these in an interview costs you the question

  • Calls server/discover a handshake every client must send first
  • Says the client and server negotiate a version during discovery
  • Claims a discover call opens a session for later requests
  • Forgets DiscoverResult must carry ttlMs and cacheScope
  • Treats the serverInfo name and version as trusted identity

context