Skip to content
Merged
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ or called. Without overrides, all discovered tools are available. Set the server
`disableTools: true` to expose no tools, regardless of overrides. Prompts and
resources are unaffected.

Each server accepts an optional `discoveryRevision` string (default empty, not
interpolated). Set it to a fresh revision to trigger rediscovery when the updated
configuration is next used. It changes the complete configuration fingerprint,
so all components and feature lists are rediscovered and subsequent stateful
calls use new downstream runtimes. Reusing an earlier value can reuse its cached
catalog. The value is not sent to downstream servers.

## Running

Build and run the HTTP frontend:
Expand Down
22 changes: 22 additions & 0 deletions catalog/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ func TestFingerprintIsStableCompleteAndSecretSafe(t *testing.T) {
}
}

func TestRegistryDiscoveryRevisionTriggersRediscovery(t *testing.T) {
discoverer := &countingDiscoverer{started: make(chan struct{}), release: make(chan struct{})}
close(discoverer.release)
registry := catalog.NewRegistry(discoverer)
defer registry.Close()
for i, field := range []string{"", " discoveryRevision: revision-1\n"} {
cfg, err := config.Load([]byte("servers:\n - name: fixture\n url: https://example.invalid\n"+field), config.LoadOptions{})
if err != nil {
t.Fatal(err)
}
for range 2 {
_, _, err := registry.Get(t.Context(), cfg)
if err != nil {
t.Fatal(err)
}
}
if got := discoverer.Count(); got != i+1 {
t.Fatalf("discoveries = %d, want %d", got, i+1)
}
}
}

func TestRegistryDeduplicatesConcurrentCompilation(t *testing.T) {
discoverer := &countingDiscoverer{started: make(chan struct{}), release: make(chan struct{})}
registry := catalog.NewRegistry(discoverer)
Expand Down
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type Config struct {

// Server describes one component MCP server.
type Server struct {
// DiscoveryRevision participates in the complete configuration fingerprint only.
DiscoveryRevision string
Name string
Prefix string
URL string
Expand Down Expand Up @@ -81,6 +83,7 @@ type configDTO struct {
}

type serverDTO struct {
DiscoveryRevision string `yaml:"discoveryRevision"`
Name string `yaml:"name"`
Prefix string `yaml:"prefix"`
URL string `yaml:"url"`
Expand Down
1 change: 1 addition & 0 deletions config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ func (d configDTO) runtime(lookup func(string) (string, bool)) (*Config, error)

func (d serverDTO) runtime(path string, lookup func(string) (string, bool)) (Server, error) {
server := Server{
DiscoveryRevision: d.DiscoveryRevision,
Name: strings.TrimSpace(d.Name),
Prefix: d.Prefix,
Headers: make(map[string]string, len(d.Headers)),
Expand Down
Loading