Skip to content

Commit e838379

Browse files
clean capabilities on config reload
1 parent 1f88d26 commit e838379

File tree

2 files changed

+32
-81
lines changed

2 files changed

+32
-81
lines changed

cmd/docker-mcp/internal/gateway/run.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ type Gateway struct {
4444

4545
sessionCacheMu sync.RWMutex
4646
sessionCache map[*mcp.ServerSession]*ServerSessionCache
47+
48+
// Track registered capabilities for cleanup during reload
49+
registeredToolNames []string
50+
registeredPromptNames []string
51+
registeredResourceURIs []string
52+
registeredResourceTemplateURIs []string
4753
}
4854

4955
func NewGateway(config Config, docker docker.Client) *Gateway {
@@ -135,7 +141,6 @@ func (g *Gateway) Run(ctx context.Context) error {
135141
log("- Client initialized: ", ss.ID())
136142
g.ListRoots(ctx, ss)
137143
},
138-
PageSize: 100,
139144
HasPrompts: true,
140145
HasResources: true,
141146
HasTools: true,
@@ -255,16 +260,40 @@ func (g *Gateway) reloadConfiguration(ctx context.Context, configuration Configu
255260
// Clear existing capabilities and register new ones
256261
// Note: The new SDK doesn't have bulk set methods, so we register individually
257262

263+
// Clear all existing capabilities by tracking them in the Gateway struct
264+
if g.registeredToolNames != nil {
265+
g.mcpServer.RemoveTools(g.registeredToolNames...)
266+
}
267+
if g.registeredPromptNames != nil {
268+
g.mcpServer.RemovePrompts(g.registeredPromptNames...)
269+
}
270+
if g.registeredResourceURIs != nil {
271+
g.mcpServer.RemoveResources(g.registeredResourceURIs...)
272+
}
273+
if g.registeredResourceTemplateURIs != nil {
274+
g.mcpServer.RemoveResourceTemplates(g.registeredResourceTemplateURIs...)
275+
}
276+
277+
// Reset tracking slices
278+
g.registeredToolNames = nil
279+
g.registeredPromptNames = nil
280+
g.registeredResourceURIs = nil
281+
g.registeredResourceTemplateURIs = nil
282+
283+
// Add new capabilities and track them
258284
for _, tool := range capabilities.Tools {
259285
g.mcpServer.AddTool(tool.Tool, tool.Handler)
286+
g.registeredToolNames = append(g.registeredToolNames, tool.Tool.Name)
260287
}
261288

262289
for _, prompt := range capabilities.Prompts {
263290
g.mcpServer.AddPrompt(prompt.Prompt, prompt.Handler)
291+
g.registeredPromptNames = append(g.registeredPromptNames, prompt.Prompt.Name)
264292
}
265293

266294
for _, resource := range capabilities.Resources {
267295
g.mcpServer.AddResource(resource.Resource, resource.Handler)
296+
g.registeredResourceURIs = append(g.registeredResourceURIs, resource.Resource.URI)
268297
}
269298

270299
// Resource templates are handled as regular resources in the new SDK
@@ -277,6 +306,7 @@ func (g *Gateway) reloadConfiguration(ctx context.Context, configuration Configu
277306
MIMEType: template.ResourceTemplate.MIMEType,
278307
}
279308
g.mcpServer.AddResourceTemplate(resource, template.Handler)
309+
g.registeredResourceTemplateURIs = append(g.registeredResourceTemplateURIs, resource.URITemplate)
280310
}
281311

282312
g.health.SetHealthy()

cmd/docker-mcp/internal/gateway/transport.go

Lines changed: 1 addition & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@ import (
88
"strings"
99
"sync"
1010

11-
"github.com/modelcontextprotocol/go-sdk/jsonrpc"
1211
"github.com/modelcontextprotocol/go-sdk/mcp"
1312

1413
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/health"
1514
)
1615

1716
func (g *Gateway) startStdioServer(ctx context.Context, _ io.Reader, _ io.Writer) error {
18-
transport := newContextAwareStdioTransport(ctx)
17+
transport := mcp.NewStdioTransport()
1918
return g.mcpServer.Run(ctx, transport)
2019
}
2120

@@ -130,81 +129,3 @@ func healthHandler(state *health.State) http.HandlerFunc {
130129
}
131130
}
132131
}
133-
134-
// contextAwareStdioTransport is a custom stdio transport that handles context cancellation properly
135-
type contextAwareStdioTransport struct {
136-
ctx context.Context
137-
}
138-
139-
func newContextAwareStdioTransport(ctx context.Context) *contextAwareStdioTransport {
140-
return &contextAwareStdioTransport{ctx: ctx}
141-
}
142-
143-
func (t *contextAwareStdioTransport) Connect(ctx context.Context) (mcp.Connection, error) {
144-
// Create the original connection once
145-
transport := mcp.NewStdioTransport()
146-
originalConn, err := transport.Connect(ctx)
147-
if err != nil {
148-
return nil, err
149-
}
150-
151-
return newContextAwareStdioConn(t.ctx, originalConn), nil
152-
}
153-
154-
// contextAwareStdioConn wraps the original connection with context-aware reading
155-
type contextAwareStdioConn struct {
156-
ctx context.Context
157-
originalConn mcp.Connection
158-
}
159-
160-
func newContextAwareStdioConn(ctx context.Context, originalConn mcp.Connection) *contextAwareStdioConn {
161-
return &contextAwareStdioConn{
162-
ctx: ctx,
163-
originalConn: originalConn,
164-
}
165-
}
166-
167-
func (c *contextAwareStdioConn) SessionID() string {
168-
return c.originalConn.SessionID()
169-
}
170-
171-
func (c *contextAwareStdioConn) Read(ctx context.Context) (jsonrpc.Message, error) {
172-
// Create a channel to read from the original connection in a separate goroutine
173-
type result struct {
174-
msg jsonrpc.Message
175-
err error
176-
}
177-
178-
ch := make(chan result, 1)
179-
go func() {
180-
msg, err := c.originalConn.Read(context.Background())
181-
ch <- result{msg, err}
182-
}()
183-
184-
// Wait for either context cancellation or read completion
185-
select {
186-
case <-ctx.Done():
187-
return nil, ctx.Err()
188-
case <-c.ctx.Done():
189-
return nil, c.ctx.Err()
190-
case res := <-ch:
191-
return res.msg, res.err
192-
}
193-
}
194-
195-
func (c *contextAwareStdioConn) Write(ctx context.Context, msg jsonrpc.Message) error {
196-
// Check context first
197-
select {
198-
case <-ctx.Done():
199-
return ctx.Err()
200-
case <-c.ctx.Done():
201-
return c.ctx.Err()
202-
default:
203-
}
204-
205-
return c.originalConn.Write(ctx, msg)
206-
}
207-
208-
func (c *contextAwareStdioConn) Close() error {
209-
return c.originalConn.Close()
210-
}

0 commit comments

Comments
 (0)