Skip to content

Commit 5c0db82

Browse files
committed
wip: address feedback and fix
Signed-off-by: Nick Hale <[email protected]>
1 parent 8fabd55 commit 5c0db82

File tree

24 files changed

+538
-333
lines changed

24 files changed

+538
-333
lines changed

apiclient/types/mcpserver.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ type CatalogComponentServer struct {
6565
CatalogEntryID string `json:"catalogEntryID"`
6666
Manifest MCPServerCatalogEntryManifest `json:"manifest"`
6767
ToolOverrides []ToolOverride `json:"toolOverrides,omitempty"`
68-
Enabled bool `json:"enabled,omitempty"`
68+
Disabled bool `json:"disabled,omitempty"`
6969
}
7070

7171
type CompositeRuntimeConfig struct {
@@ -76,7 +76,7 @@ type ComponentServer struct {
7676
CatalogEntryID string `json:"catalogEntryID"`
7777
Manifest MCPServerManifest `json:"manifest"`
7878
ToolOverrides []ToolOverride `json:"toolOverrides,omitempty"`
79-
Enabled bool `json:"enabled,omitempty"`
79+
Disabled bool `json:"disabled,omitempty"`
8080
}
8181

8282
type MCPServerCatalogEntry struct {
@@ -419,7 +419,7 @@ func MapCatalogEntryToServer(catalogEntry MCPServerCatalogEntryManifest, userURL
419419
CatalogEntryID: catalogComponent.CatalogEntryID,
420420
Manifest: componentServerManifest,
421421
ToolOverrides: catalogComponent.ToolOverrides,
422-
Enabled: true,
422+
Disabled: false,
423423
}
424424
}
425425

pkg/api/handlers/mcp.go

Lines changed: 124 additions & 75 deletions
Large diffs are not rendered by default.

pkg/api/handlers/mcpcatalogs.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,6 @@ func (h *MCPCatalogHandler) CreateEntry(req api.Context) error {
318318
}
319319

320320
func (h *MCPCatalogHandler) UpdateEntry(req api.Context) error {
321-
// TODO(cmcp): Update this handler to handle updating composite entries
322321
catalogName := req.PathValue("catalog_id")
323322
workspaceID := req.PathValue("workspace_id")
324323
entryName := req.PathValue("entry_id")
@@ -683,7 +682,10 @@ func (h *MCPCatalogHandler) GenerateToolPreviews(req api.Context) error {
683682
catalogName = req.PathValue("catalog_id")
684683
workspaceID = req.PathValue("workspace_id")
685684
entryName = req.PathValue("entry_id")
686-
dryRun = req.Request.URL.Query().Get("dryRun") == "true"
685+
// "dryRun" lets us get the previews for an MCP server without updating its CatalogEntry.
686+
// This is used when we populate the tools for individual MCP servers when creating a composite CatalogEntry
687+
// (configuring tool overrides).
688+
dryRun = req.Request.URL.Query().Get("dryRun") == "true"
687689
)
688690

689691
// Verify the scope exists
@@ -814,7 +816,6 @@ func (h *MCPCatalogHandler) generateCompositeToolPreviews(req api.Context, entry
814816
}
815817

816818
if oauthURL != "" {
817-
// TODO(cmcp): Figure out how to handle composite OAuth when generating previews for composite servers
818819
return types.NewErrBadRequest("MCP server requires OAuth authentication")
819820
}
820821

@@ -828,7 +829,6 @@ func (h *MCPCatalogHandler) generateCompositeToolPreviews(req api.Context, entry
828829
return fmt.Errorf("failed to generate tool preview: %w", err)
829830
}
830831

831-
// TODO(cmcp): Apply tool overrides to the tool preview
832832
compositeToolPreviews = append(compositeToolPreviews, toolPreview...)
833833
}
834834

@@ -858,7 +858,10 @@ func (h *MCPCatalogHandler) GenerateToolPreviewsOAuthURL(req api.Context) error
858858
catalogName = req.PathValue("catalog_id")
859859
workspaceID = req.PathValue("workspace_id")
860860
entryName = req.PathValue("entry_id")
861-
dryRun = req.Request.URL.Query().Get("dryRun") == "true"
861+
// "dryRun" lets us get the previews for an MCP server without updating its CatalogEntry.
862+
// This is used when we populate the tools for individual MCP servers when creating a composite CatalogEntry
863+
// (configuring tool overrides).
864+
dryRun = req.Request.URL.Query().Get("dryRun") == "true"
862865
)
863866

864867
// Verify the scope exists
@@ -1019,7 +1022,6 @@ func normalizeMCPCatalogEntryName(name string) string {
10191022
func (h *MCPCatalogHandler) populateComponentManifests(req api.Context, manifest *types.MCPServerCatalogEntryManifest, catalogName, workspaceID string) error {
10201023
// For each component server, fetch its catalog entry and populate the manifest
10211024
for i := range manifest.CompositeConfig.ComponentServers {
1022-
// TODO(cmcp): Handle multi-user component servers, these won't have a catalog entry ID, but will instead point directly to an MCP server
10231025
component := &manifest.CompositeConfig.ComponentServers[i]
10241026

10251027
var entry v1.MCPServerCatalogEntry

pkg/api/handlers/mcpgateway/clientmessagehandler.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ func (c *clientMessageHandler) handleMessage(ctx context.Context, msg nmcp.Messa
185185

186186
var ch <-chan nmcp.Message
187187
if msg.ID != nil {
188+
// Generate a new random UUID.
189+
// We do this to support composite servers, where multiple messages can have the same ID.
188190
msg.ID = uuid.NewString()
189191
ch = c.pendingRequests.WaitFor(msg.ID)
190192
defer c.pendingRequests.Done(msg.ID)

pkg/api/handlers/mcpgateway/composite.go

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
5858
}
5959

6060
type componentClient struct {
61-
serverContext
61+
messageContext
6262
*mcp.Client
6363
}
6464
var (
@@ -150,17 +150,12 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
150150
)
151151
if err != nil {
152152
log.Errorf("Failed to get client for server %s: %v", componentServer.mcpServer.Name, err)
153-
if componentServer.enabled {
154-
// Enabled components must initialize successfully
155-
return
156-
}
157-
// Disabled components can fail silently
158-
continue
153+
return
159154
}
160155

161156
clients[componentKey] = componentClient{
162-
serverContext: componentServer,
163-
Client: client,
157+
messageContext: componentServer,
158+
Client: client,
164159
}
165160
}
166161

@@ -185,7 +180,6 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
185180
}
186181
return
187182
}
188-
189183
}
190184

191185
// All components responded, reply with the last result
@@ -248,6 +242,7 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
248242
Code: -32603,
249243
Message: fmt.Sprintf("failed to reply to composite server %s: %v", m.mcpID, err),
250244
}
245+
return
251246
}
252247

253248
result = compositeResult
@@ -265,7 +260,6 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
265260
compositePrompts = append(compositePrompts, m.toCompositePrompt(componentKey, prompt))
266261
}
267262
}
268-
269263
compositeResult := nmcp.ListPromptsResult{
270264
Prompts: compositePrompts,
271265
}
@@ -276,12 +270,11 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
276270
Message: fmt.Sprintf("failed to reply to composite server %s: %v", m.mcpID, err),
277271
}
278272
}
279-
280273
result = compositeResult
281274
return
282275
case methodPromptsGet:
283276
var compositeRequest nmcp.GetPromptRequest
284-
if err := json.Unmarshal(msg.Params, &compositeRequest); err != nil {
277+
if err = json.Unmarshal(msg.Params, &compositeRequest); err != nil {
285278
err = &nmcp.RPCError{
286279
Code: -32602,
287280
Message: fmt.Sprintf("Failed to unmarshal get prompt request: %v", err),
@@ -309,11 +302,11 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
309302

310303
componentRequest := m.toComponentGetPromptRequest(componentKey, compositeRequest)
311304

312-
b, err := json.Marshal(componentRequest)
313-
if err != nil {
305+
b, marshalErr := json.Marshal(componentRequest)
306+
if marshalErr != nil {
314307
err = &nmcp.RPCError{
315308
Code: -32603,
316-
Message: fmt.Sprintf("failed to marshal request for server %s: %v", client.mcpServer.Name, err),
309+
Message: fmt.Sprintf("failed to marshal request for server %s: %v", client.mcpServer.Name, marshalErr),
317310
}
318311
return
319312
}
@@ -331,22 +324,23 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
331324
Code: -32603,
332325
Message: fmt.Sprintf("failed to reply to composite server %s: %v", m.mcpID, err),
333326
}
327+
return
334328
}
335329

336330
result = componentResult
337331
return
338332
case methodToolsList:
339333
var compositeTools []nmcp.Tool
340334
for componentKey, client := range clients {
341-
var result nmcp.ListToolsResult
342-
if err = client.Session.Exchange(ctx, methodToolsList, &msg, &result); err != nil {
335+
var lr nmcp.ListToolsResult
336+
if err = client.Session.Exchange(ctx, methodToolsList, &msg, &lr); err != nil {
343337
log.Errorf("Failed to send %s message to server %s: %v", msg.Method, client.mcpID, err)
344338
return
345339
}
346-
for _, tool := range result.Tools {
347-
compositeTool, err := m.toCompositeTool(componentKey, tool)
348-
if err != nil {
349-
err = fmt.Errorf("failed to override tool %s: %w", tool.Name, err)
340+
for _, tool := range lr.Tools {
341+
compositeTool, convErr := m.toCompositeTool(componentKey, tool)
342+
if convErr != nil {
343+
err = fmt.Errorf("failed to override tool %s: %w", tool.Name, convErr)
350344
return
351345
}
352346

@@ -370,13 +364,13 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
370364
Code: -32603,
371365
Message: fmt.Sprintf("failed to reply to composite server %s: %v", m.mcpID, err),
372366
}
367+
return
373368
}
374-
375369
result = compositeResult
376370
return
377371
case methodToolsCall:
378372
var compositeRequest nmcp.CallToolRequest
379-
if err := json.Unmarshal(msg.Params, &compositeRequest); err != nil {
373+
if err = json.Unmarshal(msg.Params, &compositeRequest); err != nil {
380374
err = &nmcp.RPCError{
381375
Code: -32602,
382376
Message: fmt.Sprintf("Failed to unmarshal tool call request: %v", err),
@@ -402,7 +396,8 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
402396
return
403397
}
404398

405-
componentRequest, err := m.toComponentCallToolRequest(componentKey, compositeRequest)
399+
var componentRequest *nmcp.CallToolRequest
400+
componentRequest, err = m.toComponentCallToolRequest(componentKey, compositeRequest)
406401
if err != nil {
407402
log.Errorf("Failed to convert tool call request to component tool call request: %v", err)
408403
return
@@ -412,13 +407,14 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
412407
Code: -32602,
413408
Message: fmt.Sprintf("Unknown tool: %s", compositeRequest.Name),
414409
}
410+
return
415411
}
416412

417-
b, err := json.Marshal(componentRequest)
418-
if err != nil {
413+
b, marshalErr := json.Marshal(componentRequest)
414+
if marshalErr != nil {
419415
err = &nmcp.RPCError{
420416
Code: -32603,
421-
Message: fmt.Sprintf("failed to marshal request for server %s: %v", client.mcpServer.Name, err),
417+
Message: fmt.Sprintf("failed to marshal request for server %s: %v", client.mcpServer.Name, marshalErr),
422418
}
423419
return
424420
}
@@ -436,6 +432,7 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
436432
Code: -32603,
437433
Message: fmt.Sprintf("failed to reply to composite server %s: %v", m.mcpID, err),
438434
}
435+
return
439436
}
440437

441438
result = componentResult
@@ -446,8 +443,8 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
446443
result = nmcp.Notification{}
447444
for _, client := range clients {
448445
if err = client.Session.Exchange(ctx, msg.Method, &msg, &result); err != nil {
449-
log.Errorf("Failed to send %s message to server %s: %v", msg.Method, client.mcpID, err)
450-
return
446+
log.Warnf("Failed to send %s message to server %s: %v", msg.Method, client.mcpID, err)
447+
continue
451448
}
452449
}
453450

@@ -466,7 +463,6 @@ func (h *Handler) onCompositeMessage(ctx context.Context, msg nmcp.Message, m me
466463
Message: "Method not allowed",
467464
}
468465
}
469-
470466
}
471467

472468
func normalizeName(name string) string {
@@ -505,15 +501,20 @@ func mergeInitializeResults(composite nmcp.InitializeResult, component nmcp.Init
505501
}
506502
}
507503

504+
// Merge Logging capability
505+
if component.Capabilities.Logging != nil {
506+
composite.Capabilities.Logging = &struct{}{}
507+
}
508+
508509
return composite
509510
}
510511

511512
type compositeContext struct {
512-
componentServers []serverContext
513+
componentServers []messageContext
513514
toolOverrides map[string]otypes.ToolOverride
514515
}
515516

516-
func newCompositeContext(config *otypes.CompositeRuntimeConfig, componentServers []serverContext) compositeContext {
517+
func newCompositeContext(config *otypes.CompositeRuntimeConfig, componentServers []messageContext) compositeContext {
517518
compositeContext := compositeContext{
518519
componentServers: componentServers,
519520
toolOverrides: make(map[string]otypes.ToolOverride),

0 commit comments

Comments
 (0)