Class McpRestServlet

java.lang.Object
jakarta.servlet.GenericServlet
All Implemented Interfaces:
jakarta.servlet.Servlet, jakarta.servlet.ServletConfig, Serializable, BasicUniversalConfig, DefaultConfig, DefaultHtmlConfig, ReadinessStateAware
Direct Known Subclasses:
ExampleMcpServer

public abstract class McpRestServlet extends AbstractMcpRestServlet
Abstract MCP servlet bound to protocol revision 2026-07-28.

Subclass this (rather than the neutral AbstractMcpRestServlet) to expose a 2026-07-28 endpoint at POST /; the revision binding is a compile-time choice made by which class you extend.

URI and polymorphic-type serializer policy (addBeanTypes and uriResolution="NONE") is inherited centrally from the neutral AbstractMcpRestServlet; the only revision-specific behavior added here is notification empty-body processing, below.

Registers McpNotificationResponseProcessor so a null dispatch result (a JSON-RPC notification) renders as a genuinely empty HTTP body instead of the framework's default four-byte null JSON literal.

Example:
  • Constructor Details

  • Method Details

    • revision

      protected final McpRevision revision()
      Description copied from class: AbstractMcpRestServlet
      The MCP protocol revision this servlet speaks.

      Supplied by the revision-specific subclass a consumer extends (for example org.apache.juneau.rest.server.mcp.v20250618.McpRestServlet); consumers do not normally implement this themselves.

      Specified by:
      revision in class AbstractMcpRestServlet
      Returns:
      The bound revision. Never null.
    • mcpTraceContextExtractor

      Publishes this revision's stable TraceContextExtractor so an active TracerHook (via RestOpInvoker) recognizes this endpoint's resolved JsonRpcRequest argument and its params._meta carrier before span creation.

      A no-op when no TracerHook bean is registered — see the no-tracer fast-path contract documented on TracerHook.

      Returns:
      This revision's shared TraceContextExtractor. Never null.
    • createMcpOptions

      Factory method for the behavior configuration published by getMcpOptions().

      Override to customize any of capabilities/instructions/cache/mrtr/subscriptions/broker in one place. The default returns a plain McpOptions (all defaults).

      Runs eagerly during servlet/RestContext initialization, not lazily on first request: getMcpOptions() is itself a @Bean hook, so RestContext's constructor invokes it — and therefore this method — while walking the resource's @Bean methods at startup, strictly before the embedded server's start() returns. Do not write an override that depends on runtime request state or on a port/address only known after start() (e.g. a Jetty ephemeral port); that state does not exist yet when this hook runs.

      Must be side-effect-free and idempotent, exactly like AbstractMcpRestServlet.createMcpConfig(): getMcpOptions() calls this hook at most once per servlet instance and publishes the result via a lock-free AtomicReference.compareAndSet(V, V), so under a first-access race it may be invoked by more than one thread, but only one resulting instance is ever published and returned.

      Returns:
      A non-null options instance.
    • getMcpOptions

      public final McpOptions getMcpOptions()
      Returns this servlet's binding-owned McpOptions, built eagerly during initialization.

      Because this method is annotated @Bean, RestContext's constructor invokes it (and therefore createMcpOptions()) while the servlet is initializing — strictly before the embedded server's start() returns. This is not deferred to the first incoming HTTP request, despite the internal caching below; a createMcpOptions() override must not depend on runtime request/port state, since none exists yet at that point. The AtomicReference-backed publish below exists only to make repeat/racing invocations of this @Bean method idempotent — every call after the first returns the same instance, mirroring exactly how getMcpConfig() memoizes — not to defer construction itself. Also published as a @Bean so downstream plumbing (getSubscriptionBroker(), subscriptions/listen dispatch) can resolve it uniformly through the BeanStore. This @Bean publication is authoritative and takes precedence over any Spring @Bean McpOptions in the app context: a Spring user who wants their configuration honored must inject it INTO createMcpOptions()'s override (for example an @Autowired field read from there), not publish a bare @Bean McpOptions in their Spring context and expect it to be picked up instead.

      Returns:
      The options. Never null.
      Throws:
      IllegalStateException - If createMcpOptions() returns null.
    • getSubscriptionBroker

      Returns this servlet's lazily-published, binding-owned subscription broker.

      Reads getMcpOptions()'s memoized McpOptions.getSubscriptionBroker(): an explicitly configured broker is returned as-is; otherwise one is derived and memoized per-binding, sized from McpOptions.getSubscriptions()'s queue bound (see McpOptions.resolveSubscriptionBroker()).

      Overrides:
      getSubscriptionBroker in class AbstractMcpRestServlet
      Returns:
      The subscription broker. Never null.
    • mcpResourceServerAuth

      public final void mcpResourceServerAuth(jakarta.servlet.http.HttpServletRequest req, jakarta.servlet.http.HttpServletResponse res)
      OAuth 2.1 resource-server bearer gate for the MCP POST / endpoint.

      A no-op unless RS auth is enabled; when enabled, every request that is a POST, or that claims one (the dedicated MCP servlet's only writable surface) must present a valid bearer token or it is rejected with 401 / 403 and a WWW-Authenticate: Bearer ... challenge (see McpResourceServerSupport.gateMcpServlet(McpResourceServerConfig, HttpServletRequest, HttpServletResponse)). The gate treats a POST claim on any override channel (?method= or any of the four override headers, any value) as a POST, rather than reading only the raw servlet method, so a method-override cannot slip past it. The well-known GET routes below are intentionally left unauthenticated so a client can discover the PRM document.

      Parameters:
      req - The raw HTTP request.
      res - The raw HTTP response (used to emit the challenge header on rejection).
    • mcpProtectedResourceMetadataRoot

      Serves the RFC 9728 Protected Resource Metadata document at the root well-known location (SEP-2351 fallback).

      This route (and its path-inserted sibling) is registered unconditionally, but returns 404 whenever RS auth is disabled — so a non-opted-in servlet claims the well-known namespace but exposes no observable metadata.

      Parameters:
      req - The REST request.
      Returns:
      The PRM document.
    • mcpProtectedResourceMetadataPathInserted

      Serves the RFC 9728 Protected Resource Metadata document at the path-inserted well-known location (SEP-2351), but only when the requested suffix matches this resource's expected well-known path; otherwise 404.
      Parameters:
      req - The REST request.
      Returns:
      The PRM document.