Skip to content

feat: answer server/discover for connection-less capability discovery - #235

Draft
sideeffffect wants to merge 1 commit into
softwaremill:masterfrom
sideeffffect:feat/server-discover
Draft

sideeffffect wants to merge 1 commit into
softwaremill:masterfrom
sideeffffect:feat/server-discover

Conversation

@sideeffffect

Copy link
Copy Markdown

Part of the MCP roadmap's HTTP-native / stateless direction. Relates to #163.

Context

chimp's server is already stateless: McpHandler handles each JSON-RPC request on its own, stores no session, issues no Mcp-Session-Id, and doesn't require an initialize handshake before other methods. So the 2026-07-28 "stateless architecture" is largely chimp's existing model — a request can land on any instance behind a plain round-robin load balancer.

The one concrete primitive it was missing is the discovery entry point.

What

Handle the server/discover method: a client or proxy can learn the server's capabilities up front, without negotiating a session, before doing any real work. It returns the same serverInfo, capabilities and protocolVersion payload as initialize (the initialize capability computation is refactored into a shared discoveryResult helper, so the two can't drift).

Additive — an extra method the server answers — so it doesn't affect the 2025-11-25 conformance suite or existing clients.

Tests

  • McpHandlerSpec gets a case asserting server/discover returns the server info + capabilities with no prior initialize.
  • sbt scalafmtCheckAll compile Test/compile compileDocs and the full McpHandlerSpec (33 cases) pass locally.

Notes

The exact server/discover result shape in the 2026-07-28 RC isn't fully pinned down publicly yet; mirroring the initialize result (capabilities + serverInfo + protocolVersion + instructions) is the natural, forward-compatible choice. Happy to adjust to the final schema. Documented under server capabilities. Draft/WIP.

🤖 Generated with Claude Code

The chimp server is already stateless: it stores no session, issues no
Mcp-Session-Id, and does not require an initialize handshake before other
methods. Add support for the `server/discover` method so a client or proxy can
learn the server's capabilities up front without negotiating a session; it
returns the same serverInfo, capabilities and protocol version as initialize.

This aligns with the 2026-07-28 stateless architecture direction. Tracks softwaremill#163.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sideeffffect sideeffffect changed the title feat: answer server/discover for connection-less capability discovery WIP: feat: answer server/discover for connection-less capability discovery Aug 28, 2026
@sideeffffect
sideeffffect marked this pull request as ready for review August 31, 2026 17:08
@sideeffffect sideeffffect changed the title WIP: feat: answer server/discover for connection-less capability discovery feat: answer server/discover for connection-less capability discovery Aug 31, 2026
@sideeffffect

Copy link
Copy Markdown
Author

Hello @kubinio123 , what do you think about this PR? Could I ask you for a review? 🙏

@kubinio123 kubinio123 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hi @sideeffffect, thanks for sending this in. Your intention is a groundwork for 2026-07-28 protocol version support?

I think that this compatibility matrix is the source of truth for this https://modelcontextprotocol.io/specification/2026-07-28/basic/versioning#compatibility-matrix.

Chimp is currently a legacy server. Modern / dual-era client (calling server/discover) against legacy server should get an error back.

@sideeffffect
sideeffffect marked this pull request as draft September 7, 2026 20:08
kubinio123 pushed a commit that referenced this pull request Sep 15, 2026
…26-07-28 (T1, T2) (#238)

* feat: dual-era support for the 2026-07-28 protocol version

Make chimp a dual-era server: alongside the legacy initialize handshake
(2025-11-25 and earlier), it now speaks the modern 2026-07-28 revision, where
each request carries its protocol version in _meta.

- ProtocolVersion.V2026_07_28 + a `supported` list; `Latest` still points at the
  latest legacy revision used by the initialize handshake.
- server/discover returns a DiscoverResult (supported versions, capabilities,
  serverInfo in _meta), so a modern/dual-era client no longer gets an error from
  a chimp server (addresses the #235 review).
- A request declaring an unsupported protocol version in
  _meta[io.modelcontextprotocol/protocolVersion] is rejected with
  UnsupportedProtocolVersion (-32022) listing the supported versions.
- New protocol types (DiscoverResult, CacheScope, ProtocolMeta) are validated
  against the official 2026-07-28 JSON schema (Schema2026ConformanceSpec),
  following the existing schema-conformance convention; the 2025-11-25 types are
  left untouched.

WIP: modern client mode, HTTP transport-level validation (headers / 400
semantics), resultType on all results, and packaging extensions (e.g. Tasks) as
separate modules are follow-ups. Groundwork for #163.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: type DiscoverResult.ttlMs as FiniteDuration

Keep the domain type while the wire stays integer milliseconds, via a
file-private millis codec. supportedVersions stays List[String] on purpose: the
field must carry version strings the receiver may not recognise (a newer peer),
which a closed ProtocolVersion enum would reject on decode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: add DiscoverResult.getSupportedVersions

Parses the raw supportedVersions strings into Either[String, ProtocolVersion]:
Right for a known version, Left with the raw string for an unrecognised one, so
callers get the typed view without the wire field rejecting newer versions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: cover the legacy negotiation path for dual-era support

Assert that a classic initialize (and any request whose _meta omits the modern
protocol version) is served via the legacy handshake, not rejected with
UnsupportedProtocolVersion (-32022) — the official-SDK-client interop path that
surfaces first in practice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: align dual-era groundwork with T1 (#239) and T2 (#240)

T1: make V2026_07_28 the Latest, add LatestLegacy (V2025_11_25) which the
initialize negotiation answers, and add release ordering so code can branch with
`version >= V2026_07_28`. The client and legacy handshake keep proposing the
latest legacy version.

T2: extract SchemaConformance as a shared trait parameterised by the schema
resource path, with one concrete spec per version (SchemaConformanceSpec for
2025-11-25, Schema2026ConformanceSpec for 2026-07-28).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: default streaming HTTP transports to LatestLegacy

The Pekko, Ox and ZIO streaming HTTP transports defaulted `protocolVersion`
to `ProtocolVersion.Latest` (2026-07-28), so their GET SSE stream carried
`MCP-Protocol-Version: 2026-07-28`. A legacy (2025-11-25-era) server rejects
that with HTTP 400 ("Unsupported protocol version"), which broke every HTTP
integration test in these modules.

Align them with the base `ClientHttpTransport`, which already defaults to
`ProtocolVersion.LatestLegacy`, and update the Pekko/ZIO HTTP integration
specs that explicitly passed `Latest` to the test server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: narrow PR to T1 and T2 scope per review

Address #238 review: keep this PR to
the protocol-version enum (T1, #239) and the per-version schema conformance
(T2, #240). Move the request metadata / result type / cache hints / error codes
/ discover datatypes (T4, #242) and the server-side handling with its docs
(T8, #246) into a stacked PR.

- ProtocolVersion: drop redundant comments, revert `negotiate` to its base form
  (negotiation reworks land in #246), keep enum + Latest + LatestLegacy + ordering.
- Rename conformance specs to Schema20251125ConformanceSpec /
  Schema20260728ConformanceSpec; seed the 2026-07-28 spec with shared datatypes
  so the per-version harness runs and grows with the revision-specific defs.
- Remove Versioning.scala, DiscoverResultSpec, the -32022 error code, the
  server/discover handling and capabilities docs (they return in the stacked PR).
- Keep clients defaulting to LatestLegacy so advancing Latest to 2026-07-28 does
  not regress them against legacy servers; the legacy initialize test now sends
  an explicit protocol version for the same reason.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants