Skip to content

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

Open

Description

@BrocksiNet

StatelessProtocol answers server/discover itself.
It checks the method name before dispatch and builds the DiscoverResult in a private method from the supported versions, Configuration::$capabilities and Configuration::$instructions.
Every other request, initialize included, goes through the request handlers instead.

That has two consequences:

  • A server cannot change the answer.
    initialize is answered by InitializeHandler, which a handler added with Builder::addRequestHandler() can replace, because custom handlers run before the defaults.
    There is no such handler for server/discover, so the only way to change its result is to rewrite the HTTP response after Server::run(), which depends on the wire format and has to skip streamed responses.
  • server/discover skips what every other method goes through: RequestEvent, ResponseEvent and ErrorEvent are not dispatched for it, and a failure is not mapped to an error the way a handler's failure is.

Use case

A server that serves both eras from one endpoint may need a different server/discover answer than its initialize answer.
For example, a server whose tools enable more tools for the current session cannot offer those on 2026-07-28, which has no session.
Its instructions are shared by both eras today, so they tell stateless clients to call tools that are not listed and cannot work.

Proposal

Answer server/discover with a default DiscoverHandler, like initialize is answered with InitializeHandler:

  • DiscoverHandler returns the same DiscoverResult as today.
    The modern dispatcher registers it after the custom handlers, so a handler added with addRequestHandler() that supports DiscoverRequest answers instead.
  • server/discover goes through the normal dispatch and its events, and keeps its wire behaviour: same status codes and header checks, never a stream (also when the client accepts text/event-stream), and not reachable from the handshake era.

A server can then register its own discover handler, or adjust the default answer in a ResponseEvent listener.

Alternative considered

A second configuration field, for example Builder::setModernInstructions() used by server/discover and falling back to setInstructions().
It solves the instructions case only: every other member that may differ per era (capabilities, for example) would need its own field, and Configuration would copy each public attribute per era.
A handler covers all of them with the extension point the SDK already has for initialize.

Context

Shopware serves both eras from its MCP endpoints and hides its session-bound toolset tools on 2026-07-28.
It currently swaps the instructions in the HTTP response as a workaround (shopware/shopware#22011) and would drop that once server/discover can be customized.

Activity

  1. added
    ServerIssues & PRs related to the Server component
    enhancementRequest for a new feature that's not currently supported
    2026-07-28All issues and PRs related to the spec release 2026-07-28
    on Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions