Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions mcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,43 @@ func (s *Server) getPrompt(ctx context.Context, req *GetPromptRequest) (*GetProm
return res, err
}

// Discover returns the [DiscoverResult] the server would advertise via the
// SEP-2575 server/discover method.
//
// If ss is non-nil, SupportedVersions reflects the protocol versions that
// session's transport can serve. If ss is nil, SupportedVersions is the list
// this server advertises: every version from [SupportedProtocolVersions],
// narrowed by [ServerOptions.SupportedProtocolVersions] when that option is set.
//
// Discover is intended for host introspection (for example health checks) and
// does not mutate session state. It applies only the protocol cache default
// (cacheScope "public"). Inbound server/discover RPCs still run the internal
// handler, which may establish session identity from the client request and
// which settles cache hints via [ServerOptions.SetCacheable].
//
// To customize responses to inbound server/discover requests, use
// [Server.AddReceivingMiddleware].
func (s *Server) Discover(ss *ServerSession) *DiscoverResult {
var versions []string
if ss != nil {
ss.mu.Lock()
versions = ss.supportedVersions
ss.mu.Unlock()
}
if versions == nil {
versions = slices.Clone(s.protocolVersions)
} else {
versions = slices.Clone(versions)
}
res := &DiscoverResult{
SupportedVersions: versions,
Capabilities: s.capabilities(),
Instructions: s.opts.Instructions,
}
res.normalize()
return res
}

// discover is the server-side handler for the SEP-2575 "server/discover" RPC.
//
// It returns the protocol versions supported by the underlying transport,
Expand Down Expand Up @@ -1021,11 +1058,10 @@ func (s *Server) discover(ctx context.Context, req *ServerRequest[*DiscoverParam
state.NegotiatedProtocolVersion = init.ProtocolVersion
})
}
res := &DiscoverResult{
SupportedVersions: versions,
Capabilities: s.capabilities(),
Instructions: s.opts.Instructions,
}
res := s.Discover(req.Session)
// Discover fills the protocol cache default for host callers. Hand
// resolveCacheable the unset value so SetCacheable still sees "no opinion".
res.Cacheable = Cacheable{}
s.resolveCacheable(ctx, req, &res.Cacheable)
return res, nil
}
Expand Down
56 changes: 56 additions & 0 deletions mcp/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,62 @@ func TestClientRootCapabilities(t *testing.T) {
}
}

func TestServerDiscover(t *testing.T) {
const instructions = "use carefully"
s := NewServer(&Implementation{Name: "testServer", Version: "v1.0.0"}, &ServerOptions{
Instructions: instructions,
HasTools: true,
})

t.Run("nil session uses SDK supported versions", func(t *testing.T) {
got := s.Discover(nil)
if got == nil {
t.Fatal("Discover(nil) returned nil")
}
if diff := cmp.Diff(supportedProtocolVersions, got.SupportedVersions); diff != "" {
t.Errorf("SupportedVersions mismatch (-want +got):\n%s", diff)
}
if got.Instructions != instructions {
t.Errorf("Instructions = %q, want %q", got.Instructions, instructions)
}
if got.Capabilities == nil || got.Capabilities.Tools == nil {
t.Errorf("Capabilities.Tools = %v, want non-nil when HasTools is set", got.Capabilities)
}
if got.CacheScope != "public" {
t.Errorf("CacheScope = %q, want public", got.CacheScope)
}
})

t.Run("session versions and no state mutation", func(t *testing.T) {
ctx := context.Background()
_, st := NewInMemoryTransports()
ss, err := s.Connect(ctx, st, nil)
if err != nil {
t.Fatal(err)
}
defer ss.Close()

if ss.InitializeParams() != nil {
t.Fatal("session already initialized before Discover")
}
got := s.Discover(ss)
if got == nil {
t.Fatal("Discover(session) returned nil")
}
// In-memory transport does not filter versions; session should
// advertise the full SDK list.
if diff := cmp.Diff(supportedProtocolVersions, got.SupportedVersions); diff != "" {
t.Errorf("SupportedVersions mismatch (-want +got):\n%s", diff)
}
if got.Instructions != instructions {
t.Errorf("Instructions = %q, want %q", got.Instructions, instructions)
}
if ss.InitializeParams() != nil {
t.Error("Discover(session) mutated session identity; want no side effects")
}
})
}

func TestServerRejectsDuplicateInitialize(t *testing.T) {
ctx := context.Background()

Expand Down
Loading