In MCP, what does server/discover return, and which side is required to implement it?
answer
- one call, whole server profile
- servers must, clients may
- no arguments, only _meta
- versions, capabilities, instructions, cache hints
- not a handshake, nothing is negotiated
basics
~10 sserver/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 sIn 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{
"jsonrpc": "2.0",
"id": 1,
"method": "server/discover",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}go deeper
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.
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.
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.
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