Skip to content

[Server] Answer server/discover with a request handler - #622

Open
BrocksiNet wants to merge 3 commits into
modelcontextprotocol:mainfrom
BrocksiNet:feat/discover-request-handler
Open

BrocksiNet wants to merge 3 commits into
modelcontextprotocol:mainfrom
BrocksiNet:feat/discover-request-handler

Conversation

@BrocksiNet

Copy link
Copy Markdown
Contributor

Summary

  • server/discover is answered by a new default DiscoverHandler, like initialize is answered by InitializeHandler, instead of being hard-coded in StatelessProtocol. The private StatelessProtocol::discover() is removed.
  • Overriding uses the existing mechanism: custom handlers added with addRequestHandler() run before the defaults, so a handler supporting DiscoverRequest answers instead. A ResponseEvent listener can also adjust the default answer, since server/discover now dispatches RequestEvent, ResponseEvent and ErrorEvent like every other request.
  • server/discover exists in the modern era only, so Builder::buildStateless() adds both its message class (DiscoverRequest) and its default handler there, not to the parts both eras share. The handshake era therefore still never parses server/discover: it answers it as before, and a custom discover handler is not reachable from it.
  • The wire behaviour is unchanged: the default answer is byte-identical to before (same result, resultType, caching hints and serverInfo), status codes and header validation run as before, and the answer is never streamed, also when the client accepts text/event-stream (a replacement handler that emits notifications gets them dropped, as for a client that does not read streams).
  • Docs: "Customizing server/discover" in docs/run/protocol-eras.md with a short example, and a pointer in docs/run/server-builder.md.

I considered a per-era config field (setModernInstructions()), but it only covers instructions and would copy every public attribute per era. A handler covers any member with the extension point the SDK already has.

Motivation: Shopware serves both eras and hides its session-bound toolset tools on 2026-07-28, so its initialize instructions must not reach stateless clients. Today it rewrites the server/discover JSON response as a workaround: shopware/shopware#22011

Fixes #621

Test plan

  • DiscoverHandlerTest: supports only DiscoverRequest, answers with versions, capabilities and instructions, leaves instructions out when none are set
  • StatelessProtocolTest: the default answer equals the previous wire output exactly; a handler added through the builder replaces it; request and response events are dispatched and a ResponseEvent listener can change the answer; a client accepting text/event-stream gets plain JSON even from a handler that emits a notification (fails without the change)
  • StdioDualEraTest: a custom discover handler answers a modern stdio client, and a handshake-era stdio connection still gets -32600 for server/discover
  • Existing discover tests unchanged and green
  • make tests (2960 tests)
  • make phpstan
  • make cs
  • composer validate --strict

`StatelessProtocol` answered `server/discover` itself, before dispatch, so
a server could not change the answer and the request skipped the request,
response and error events every other method goes through.

Answer it with a default `DiscoverHandler`, like `initialize` is answered
with `InitializeHandler`. `Builder::buildStateless()` registers it after
the custom handlers, so a handler added with `addRequestHandler()` that
supports `DiscoverRequest` answers instead, and adds `DiscoverRequest` to
the modern dispatcher's message factory only, so the handshake era still
does not parse `server/discover`.

The wire answer is unchanged and is never streamed, also when the client
accepts `text/event-stream`. The private `StatelessProtocol::discover()`
is removed.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-26T14:47:19.113143Z d385149 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chr-hertel chr-hertel added Server Issues & PRs related to the Server component enhancement Request for a new feature that's not currently supported 2026-07-28 All issues and PRs related to the spec release 2026-07-28 labels Oct 27, 2026
@chr-hertel
chr-hertel requested a balanced review from Copilot October 27, 2026 23:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues block the change enabling customizable modern-era discovery handlers.

No open issues 🎉

What changed in this PR

Moves server/discover into the SDK’s request-handler pipeline, addressing #621 by allowing servers to customize modern-era discovery independently of handshake-era initialization.

Changes:

  • Adds a default DiscoverHandler with modern-only registration and support for custom handlers and request events.
  • Preserves the default response format and plain-JSON behavior, including when clients accept streams.
  • Adds handler, protocol, and stdio tests, plus customization documentation.
File Description
tests/​Unit/​Server/​Transport/​StdioDualEraTest.php Tests custom discovery handling and handshake-era rejection.
tests/​Unit/​Server/​Stateless/​StatelessProtocolTest.php Tests default output, overrides, events, and non-streaming responses.
tests/​Unit/​Server/​Handler/​Request/​DiscoverHandlerTest.php Tests supported requests and configuration-derived results.
src/​Server/​Stateless/​StatelessProtocol.php Routes discovery through dispatch while preventing streaming.
src/​Server/​Handler/​Request/​DiscoverHandler.php Implements the default discovery response.
src/​Server/​Builder.php Registers discovery requests and the default handler for the modern era.
docs/​run/​server-builder.md Links to discovery customization guidance.
docs/​run/​protocol-eras.md Documents handler overrides and response-event customization.
CHANGELOG.md Records the discovery-handler change for 0.9.1.

Review effort: Balanced

…lers

The custom handler and events guides still said the SDK answers `server/discover` itself and dispatches no events for it, which `DiscoverHandler` made untrue.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2026-07-28 All issues and PRs related to the spec release 2026-07-28 enhancement Request for a new feature that's not currently supported Server Issues & PRs related to the Server component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make server/discover a request handler so servers can customize it

3 participants