@@ -21,7 +21,6 @@ type Gateway struct {
21
21
docker docker.Client
22
22
configurator Configurator
23
23
clientPool * clientPool
24
- mcpServer * server.MCPServer
25
24
health health.State
26
25
}
27
26
@@ -36,6 +35,7 @@ func NewGateway(config Config, docker docker.Client) *Gateway {
36
35
ConfigPath : config .ConfigPath ,
37
36
SecretsPath : config .SecretsPath ,
38
37
Watch : config .Watch ,
38
+ Central : config .Central ,
39
39
docker : docker ,
40
40
},
41
41
clientPool : newClientPool (config .Options , docker ),
@@ -60,13 +60,48 @@ func (g *Gateway) Run(ctx context.Context) error {
60
60
}
61
61
}
62
62
63
+ // Build a list of interceptors.
64
+ customInterceptors , err := interceptors .Parse (g .Interceptors )
65
+ if err != nil {
66
+ return fmt .Errorf ("parsing interceptors: %w" , err )
67
+ }
68
+ toolCallbacks := interceptors .Callbacks (g .LogCalls , g .BlockSecrets , customInterceptors )
69
+
70
+ // Create the MCP server.
71
+ newMCPServer := func () * server.MCPServer {
72
+ return server .NewMCPServer (
73
+ "Docker AI MCP Gateway" ,
74
+ "2.0.1" ,
75
+ server .WithToolHandlerMiddleware (toolCallbacks ),
76
+ server .WithHooks (& server.Hooks {
77
+ OnBeforeInitialize : []server.OnBeforeInitializeFunc {
78
+ func (_ context.Context , id any , _ * mcp.InitializeRequest ) {
79
+ log ("> Initializing MCP server with ID:" , id )
80
+ },
81
+ },
82
+ }),
83
+ )
84
+ }
85
+
63
86
// Read the configuration.
64
87
configuration , configurationUpdates , stopConfigWatcher , err := g .configurator .Read (ctx )
65
88
if err != nil {
66
89
return err
67
90
}
68
91
defer func () { _ = stopConfigWatcher () }()
69
92
93
+ // Central mode.
94
+ if g .Central {
95
+ log ("> Initialized in" , time .Since (start ))
96
+ if g .DryRun {
97
+ log ("Dry run mode enabled, not starting the server." )
98
+ return nil
99
+ }
100
+
101
+ return g .startCentralStreamingServer (ctx , newMCPServer , ln , configuration )
102
+ }
103
+ mcpServer := newMCPServer ()
104
+
70
105
// Which docker images are used?
71
106
// Pull them and verify them if possible.
72
107
if ! g .Static {
@@ -84,27 +119,7 @@ func (g *Gateway) Run(ctx context.Context) error {
84
119
}
85
120
}
86
121
87
- // Build a list of interceptors.
88
- customInterceptors , err := interceptors .Parse (g .Interceptors )
89
- if err != nil {
90
- return fmt .Errorf ("parsing interceptors: %w" , err )
91
- }
92
- toolCallbacks := interceptors .Callbacks (g .LogCalls , g .BlockSecrets , customInterceptors )
93
-
94
- g .mcpServer = server .NewMCPServer (
95
- "Docker AI MCP Gateway" ,
96
- "2.0.1" ,
97
- server .WithToolHandlerMiddleware (toolCallbacks ),
98
- server .WithHooks (& server.Hooks {
99
- OnBeforeInitialize : []server.OnBeforeInitializeFunc {
100
- func (_ context.Context , id any , _ * mcp.InitializeRequest ) {
101
- log ("> Initializing MCP server with ID:" , id )
102
- },
103
- },
104
- }),
105
- )
106
-
107
- if err := g .reloadConfiguration (ctx , configuration ); err != nil {
122
+ if err := g .reloadConfiguration (ctx , mcpServer , configuration , nil ); err != nil {
108
123
return fmt .Errorf ("loading configuration: %w" , err )
109
124
}
110
125
@@ -125,7 +140,7 @@ func (g *Gateway) Run(ctx context.Context) error {
125
140
continue
126
141
}
127
142
128
- if err := g .reloadConfiguration (ctx , configuration ); err != nil {
143
+ if err := g .reloadConfiguration (ctx , mcpServer , configuration , nil ); err != nil {
129
144
logf ("> Unable to list capabilities: %s" , err )
130
145
continue
131
146
}
@@ -144,24 +159,26 @@ func (g *Gateway) Run(ctx context.Context) error {
144
159
switch strings .ToLower (g .Transport ) {
145
160
case "stdio" :
146
161
log ("> Start stdio server" )
147
- return g .startStdioServer (ctx , os .Stdin , os .Stdout )
162
+ return g .startStdioServer (ctx , mcpServer , os .Stdin , os .Stdout )
148
163
149
164
case "sse" :
150
165
log ("> Start sse server on port" , g .Port )
151
- return g .startSseServer (ctx , ln )
166
+ return g .startSseServer (ctx , mcpServer , ln )
152
167
153
168
case "streaming" :
154
169
log ("> Start streaming server on port" , g .Port )
155
- return g .startStreamingServer (ctx , ln )
170
+ return g .startStreamingServer (ctx , mcpServer , ln )
156
171
157
172
default :
158
173
return fmt .Errorf ("unknown transport %q, expected 'stdio', 'sse' or 'streaming" , g .Transport )
159
174
}
160
175
}
161
176
162
- func (g * Gateway ) reloadConfiguration (ctx context.Context , configuration Configuration ) error {
177
+ func (g * Gateway ) reloadConfiguration (ctx context.Context , mcpServer * server. MCPServer , configuration Configuration , serverNames [] string ) error {
163
178
// Which servers are enabled in the registry.yaml?
164
- serverNames := configuration .ServerNames ()
179
+ if len (serverNames ) == 0 {
180
+ serverNames = configuration .ServerNames ()
181
+ }
165
182
if len (serverNames ) == 0 {
166
183
log ("- No server is enabled" )
167
184
} else {
@@ -179,12 +196,12 @@ func (g *Gateway) reloadConfiguration(ctx context.Context, configuration Configu
179
196
180
197
// Update the server's capabilities.
181
198
g .health .SetUnhealthy ()
182
- g . mcpServer .SetTools (capabilities .Tools ... )
183
- g . mcpServer .SetPrompts (capabilities .Prompts ... )
184
- g . mcpServer .SetResources (capabilities .Resources ... )
185
- g . mcpServer .RemoveAllResourceTemplates ()
199
+ mcpServer .SetTools (capabilities .Tools ... )
200
+ mcpServer .SetPrompts (capabilities .Prompts ... )
201
+ mcpServer .SetResources (capabilities .Resources ... )
202
+ mcpServer .RemoveAllResourceTemplates ()
186
203
for _ , v := range capabilities .ResourceTemplates {
187
- g . mcpServer .AddResourceTemplate (v .ResourceTemplate , v .Handler )
204
+ mcpServer .AddResourceTemplate (v .ResourceTemplate , v .Handler )
188
205
}
189
206
g .health .SetHealthy ()
190
207
0 commit comments