Skip to content

Commit 4dc99b4

Browse files
mcp: Remove resumability and introduce MissingRequiredClientCapability error data (#1005)
Fixes #966
1 parent 88c58c3 commit 4dc99b4

3 files changed

Lines changed: 31 additions & 5 deletions

File tree

mcp/protocol.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,3 +2116,18 @@ type UnsupportedProtocolVersionData struct {
21162116
// Requested is the protocol version the client asked for.
21172117
Requested string `json:"requested"`
21182118
}
2119+
2120+
// MissingRequiredClientCapabilityData is the SEP-2575 payload carried in the
2121+
// `data` field of a JSON-RPC error response with code
2122+
// [CodeMissingRequiredClientCapabilities]. The server uses it to indicate
2123+
// which client capabilities are required to process the request but were not
2124+
// declared by the client in its per-request `_meta` field.
2125+
//
2126+
// Handlers that require a specific client capability should inspect the
2127+
// per-request [ServerRequest.ClientCapabilities] and return a JSON-RPC error
2128+
// populated with this structure when the required capability is missing.
2129+
type MissingRequiredClientCapabilityData struct {
2130+
// RequiredCapabilities is the set of capabilities the server requires
2131+
// from the client to process the request.
2132+
RequiredCapabilities *ClientCapabilities `json:"requiredCapabilities"`
2133+
}

mcp/shared.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,8 @@ type RequestExtra struct {
585585
// to configure the reconnection delay.
586586
//
587587
// [SEP-1699]: https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699
588+
// This mechanism is deprecated in protocol version 2026-06-30 as the resumability
589+
// feature is removed.
588590
CloseSSEStream func(CloseSSEStreamArgs)
589591
}
590592

mcp/streamable.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,7 @@ func (s *stream) doneLocked() bool {
10591059
}
10601060

10611061
func (c *streamableServerConn) newStream(ctx context.Context, requests map[jsonrpc.ID]struct{}, id string) (*stream, error) {
1062-
if c.eventStore != nil {
1062+
if c.eventStore != nil && protocolVersionFromContext(ctx) < protocolVersion20260630 {
10631063
if err := c.eventStore.Open(ctx, c.sessionID, id); err != nil {
10641064
return nil, err
10651065
}
@@ -1366,14 +1366,13 @@ func (c *streamableServerConn) servePOST(w http.ResponseWriter, req *http.Reques
13661366
tokenInfo := auth.TokenInfoFromContext(req.Context())
13671367
isInitialize := false
13681368
var initializeProtocolVersion string
1369-
headerVersion := protocolVersionFromContext(req.Context())
13701369
for _, msg := range incoming {
13711370
if jreq, ok := msg.(*jsonrpc.Request); ok {
13721371
// Preemptively check that this is a valid request, so that we can fail
13731372
// the HTTP request. If we didn't do this, a request with a bad method or
13741373
// missing ID could be silently swallowed.
13751374
if _, err := checkRequest(jreq, serverMethodInfos); err != nil {
1376-
if headerVersion >= protocolVersion20260630 && errors.Is(err, jsonrpc2.ErrNotHandled) && jreq.IsCall() {
1375+
if protocolVersion >= protocolVersion20260630 && errors.Is(err, jsonrpc2.ErrNotHandled) && jreq.IsCall() {
13771376
writeJSONRPCError(w, http.StatusNotFound, jreq.ID, &jsonrpc.Error{
13781377
Code: jsonrpc.CodeMethodNotFound,
13791378
Message: err.Error(),
@@ -1403,6 +1402,9 @@ func (c *streamableServerConn) servePOST(w http.ResponseWriter, req *http.Reques
14031402
metaVersion, _ = meta[MetaKeyProtocolVersion].(string)
14041403
}
14051404
if protocolVersion >= protocolVersion20260630 || metaVersion != "" {
1405+
// Extract again the protcol version from the context to see what the client
1406+
// is advertising in the Mcp-Protocol-Version HTTP header.
1407+
headerVersion := protocolVersionFromContext(req.Context())
14061408
// server/discover is exempt from the stateful
14071409
// rejection as it should learn about the supported protocols from the
14081410
// DiscoverResult response.
@@ -1452,6 +1454,12 @@ func (c *streamableServerConn) servePOST(w http.ResponseWriter, req *http.Reques
14521454
// See the doc for CloseSSEStream: allow the request handler to
14531455
// explicitly close the ongoing stream.
14541456
jreq.Extra.(*RequestExtra).CloseSSEStream = func(args CloseSSEStreamArgs) {
1457+
// This mechanism was designed to trigger client reconnection with
1458+
// Last-Event-ID for server-initiated disconnect scenarios. It is
1459+
// deprecated in protocol version 2026-06-30.
1460+
if protocolVersion >= protocolVersion20260630 {
1461+
return
1462+
}
14551463
c.mu.Lock()
14561464
streamID, ok := c.requestStreams[jreq.ID]
14571465
var stream *stream
@@ -1729,7 +1737,8 @@ func (c *streamableServerConn) Write(ctx context.Context, msg jsonrpc.Message) e
17291737
// pushing down into the delivery layer.
17301738
delivered := false
17311739
var errs []error
1732-
if c.eventStore != nil {
1740+
protocolVersion := protocolVersionFromContext(ctx)
1741+
if c.eventStore != nil && protocolVersion < protocolVersion20260630 {
17331742
if err := c.eventStore.Append(ctx, c.sessionID, s.id, data); err != nil {
17341743
errs = append(errs, err)
17351744
} else {
@@ -1740,7 +1749,7 @@ func (c *streamableServerConn) Write(ctx context.Context, msg jsonrpc.Message) e
17401749
// Compute eventID for SSE streams with event store.
17411750
// Use s.lastIdx + 1 because deliverLocked increments before writing.
17421751
var eventID string
1743-
if c.eventStore != nil {
1752+
if c.eventStore != nil && protocolVersion < protocolVersion20260630 {
17441753
eventID = formatEventID(s.id, s.lastIdx+1)
17451754
}
17461755

0 commit comments

Comments
 (0)