Skip to content

Commit 65e0526

Browse files
authored
Support composite MCP servers (#4560)
This change introduces "Composite" MCP Servers and support for: - Creating composite servers by combining multiple single or remote servers - Composite 2nd level OAuth flow This change does not include support for: - Adding multi-user MCP servers to a composite server - Upgrading composite MCP server instances and their child servers Support for these will be included in follow up PRs. Addresses part of #3553 --------- Signed-off-by: Nick Hale <[email protected]>
1 parent fa988fa commit 65e0526

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+3712
-273
lines changed

apiclient/types/mcpserver.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const (
1515
RuntimeNPX Runtime = "npx"
1616
RuntimeContainerized Runtime = "containerized"
1717
RuntimeRemote Runtime = "remote"
18+
RuntimeComposite Runtime = "composite"
1819
)
1920

2021
// UVXRuntimeConfig represents configuration for UVX runtime (Python packages via uvx)
@@ -54,6 +55,30 @@ type RemoteCatalogConfig struct {
5455
Headers []MCPHeader `json:"headers,omitempty"` // Optional
5556
}
5657

58+
// CompositeCatalogConfig represents configuration for composite servers in catalog entries.
59+
type CompositeCatalogConfig struct {
60+
ComponentServers []CatalogComponentServer `json:"componentServers"`
61+
}
62+
63+
// CatalogComponentServer represents a component server in a composite server catalog entry.
64+
type CatalogComponentServer struct {
65+
CatalogEntryID string `json:"catalogEntryID"`
66+
Manifest MCPServerCatalogEntryManifest `json:"manifest"`
67+
ToolOverrides []ToolOverride `json:"toolOverrides,omitempty"`
68+
Disabled bool `json:"disabled,omitempty"`
69+
}
70+
71+
type CompositeRuntimeConfig struct {
72+
ComponentServers []ComponentServer `json:"componentServers"`
73+
}
74+
75+
type ComponentServer struct {
76+
CatalogEntryID string `json:"catalogEntryID"`
77+
Manifest MCPServerManifest `json:"manifest"`
78+
ToolOverrides []ToolOverride `json:"toolOverrides,omitempty"`
79+
Disabled bool `json:"disabled,omitempty"`
80+
}
81+
5782
type MCPServerCatalogEntry struct {
5883
Metadata
5984
Manifest MCPServerCatalogEntryManifest `json:"manifest"`
@@ -83,10 +108,23 @@ type MCPServerCatalogEntryManifest struct {
83108
NPXConfig *NPXRuntimeConfig `json:"npxConfig,omitempty"`
84109
ContainerizedConfig *ContainerizedRuntimeConfig `json:"containerizedConfig,omitempty"`
85110
RemoteConfig *RemoteCatalogConfig `json:"remoteConfig,omitempty"`
111+
CompositeConfig *CompositeCatalogConfig `json:"compositeConfig,omitempty"`
86112

87113
Env []MCPEnv `json:"env,omitempty"`
88114
}
89115

116+
// ToolOverride defines how a single component tool is exposed by the composite server
117+
type ToolOverride struct {
118+
// Name is the original tool name as returned by the component server
119+
Name string `json:"name"`
120+
// OverrideName is the tool name exposed by the composite server
121+
OverrideName string `json:"overrideName"`
122+
// Optional overrides for display
123+
OverrideDescription string `json:"overrideDescription,omitempty"`
124+
// Whether to include this tool (default true)
125+
Enabled bool `json:"enabled,omitempty"`
126+
}
127+
90128
type MCPHeader struct {
91129
Name string `json:"name"`
92130
Description string `json:"description"`
@@ -124,6 +162,7 @@ type MCPServerManifest struct {
124162
NPXConfig *NPXRuntimeConfig `json:"npxConfig,omitempty"`
125163
ContainerizedConfig *ContainerizedRuntimeConfig `json:"containerizedConfig,omitempty"`
126164
RemoteConfig *RemoteRuntimeConfig `json:"remoteConfig,omitempty"`
165+
CompositeConfig *CompositeRuntimeConfig `json:"compositeConfig,omitempty"`
127166

128167
Env []MCPEnv `json:"env,omitempty"`
129168

@@ -354,6 +393,40 @@ func MapCatalogEntryToServer(catalogEntry MCPServerCatalogEntryManifest, userURL
354393
remoteConfig.Headers = catalogEntry.RemoteConfig.Headers
355394
serverManifest.RemoteConfig = remoteConfig
356395

396+
case RuntimeComposite:
397+
if catalogEntry.CompositeConfig == nil {
398+
return serverManifest, RuntimeValidationError{
399+
Runtime: RuntimeComposite,
400+
Field: "compositeConfig",
401+
Message: "composite configuration is required for composite runtime",
402+
}
403+
}
404+
405+
// Convert CatalogComponentServer to ComponentServer
406+
componentServers := make([]ComponentServer, len(catalogEntry.CompositeConfig.ComponentServers))
407+
for i, catalogComponent := range catalogEntry.CompositeConfig.ComponentServers {
408+
// Convert the component's catalog manifest to server manifest
409+
componentServerManifest, err := MapCatalogEntryToServer(catalogComponent.Manifest, "")
410+
if err != nil {
411+
return serverManifest, RuntimeValidationError{
412+
Runtime: RuntimeComposite,
413+
Field: fmt.Sprintf("compositeConfig.componentServers[%d]", i),
414+
Message: fmt.Sprintf("failed to convert component manifest: %v", err),
415+
}
416+
}
417+
418+
componentServers[i] = ComponentServer{
419+
CatalogEntryID: catalogComponent.CatalogEntryID,
420+
Manifest: componentServerManifest,
421+
ToolOverrides: catalogComponent.ToolOverrides,
422+
Disabled: false,
423+
}
424+
}
425+
426+
serverManifest.CompositeConfig = &CompositeRuntimeConfig{
427+
ComponentServers: componentServers,
428+
}
429+
357430
default:
358431
return serverManifest, RuntimeValidationError{
359432
Runtime: catalogEntry.Runtime,

apiclient/types/zz_generated.deepcopy.go

Lines changed: 111 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/api/authz/resources.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ var apiResources = map[string][]string{
1717
"GET /api/all-mcps/servers/{mcpserver_id}/prompts/{prompt_name}",
1818
"GET /oauth/callback/{oauth_request_id}/{mcp_id}",
1919
"GET /oauth/mcp/callback",
20+
"GET /auth/mcp/composite/{mcp_id}",
21+
"GET /api/oauth/composite/{mcp_id}",
2022
"GET /mcp-connect/{mcp_id}",
2123
"POST /mcp-connect/{mcp_id}",
2224
"DELETE /mcp-connect/{mcp_id}",

0 commit comments

Comments
 (0)