Files
higress/plugins/wasm-go/pkg/mcp/server/composed_server.go

128 lines
4.6 KiB
Go

package server
import (
"fmt"
"github.com/higress-group/wasm-go/pkg/log"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/consts"
)
// ComposedMCPServer represents a server composed of tools from other servers.
type ComposedMCPServer struct {
name string // Name of the composed server (from toolSet.name)
serverTools []ServerToolConfig // Configuration of which tools to include
registry *GlobalToolRegistry // Reference to the global tool registry
config []byte // Configuration for the composed server itself (if any)
}
// NewComposedMCPServer creates a new ComposedMCPServer.
func NewComposedMCPServer(name string, serverToolsConfig []ServerToolConfig, registry *GlobalToolRegistry) *ComposedMCPServer {
return &ComposedMCPServer{
name: name,
serverTools: serverToolsConfig,
registry: registry,
}
}
// GetName returns the name of the composed server.
func (cs *ComposedMCPServer) GetName() string {
return cs.name
}
// AddMCPTool for ComposedMCPServer is a no-op as tools are defined by toolSet.
func (cs *ComposedMCPServer) AddMCPTool(name string, tool Tool) Server {
log.Warnf("AddMCPTool called on ComposedMCPServer '%s'; this is a no-op.", cs.name)
return cs
}
// GetMCPTools constructs and returns the map of tools exposed by this composed server.
// The tool names are prefixed with their original server name, e.g., "${originalServer}___${toolName}".
// The Tool instances are DescriptiveTool, only providing Description and InputSchema.
func (cs *ComposedMCPServer) GetMCPTools() map[string]Tool {
composedTools := make(map[string]Tool)
for _, stc := range cs.serverTools {
originalServerName := stc.ServerName
for _, originalToolName := range stc.Tools {
toolInfo, found := cs.registry.GetToolInfo(originalServerName, originalToolName)
if !found {
log.Warnf("Tool %s/%s not found in global registry for composed server %s", originalServerName, originalToolName, cs.name)
continue
}
composedToolName := fmt.Sprintf("%s%s%s", originalServerName, consts.ToolSetNameSplitter, originalToolName)
composedTools[composedToolName] = &DescriptiveTool{
description: toolInfo.Description,
inputSchema: toolInfo.InputSchema,
outputSchema: toolInfo.OutputSchema, // New field for MCP Protocol Version 2025-06-18
}
}
}
return composedTools
}
// SetConfig sets the configuration for the composed server itself.
func (cs *ComposedMCPServer) SetConfig(config []byte) {
cs.config = config
}
// GetConfig retrieves the configuration of the composed server itself.
func (cs *ComposedMCPServer) GetConfig(v any) {
if len(cs.config) == 0 {
return
}
if ptrBytes, ok := v.(*[]byte); ok {
*ptrBytes = cs.config
} else {
// If you need to unmarshal to a struct, you'd do it here.
// For now, keeping it simple as per previous discussions.
log.Warnf("ComposedMCPServer.GetConfig called with unhandled type for v. Config not set.")
}
}
// Clone creates a new instance of the ComposedMCPServer with the same configuration.
func (cs *ComposedMCPServer) Clone() Server {
cloned := NewComposedMCPServer(cs.name, cs.serverTools, cs.registry)
cloned.SetConfig(cs.config)
return cloned
}
// DescriptiveTool is a placeholder Tool implementation for ComposedMCPServer.
// Its Call and Create methods should never be invoked.
type DescriptiveTool struct {
description string
inputSchema map[string]any
outputSchema map[string]any // New field for MCP Protocol Version 2025-06-18
}
// Create for DescriptiveTool should not be called.
func (dt *DescriptiveTool) Create(params []byte) Tool {
log.Errorf("DescriptiveTool.Create called for tool used in ComposedMCPServer. This should not happen.")
// Return a new instance to fulfill the interface, though it's an error state.
return &DescriptiveTool{
description: dt.description,
inputSchema: dt.inputSchema,
outputSchema: dt.outputSchema,
}
}
// Call for DescriptiveTool should not be called.
func (dt *DescriptiveTool) Call(httpCtx HttpContext, server Server) error {
log.Errorf("DescriptiveTool.Call called for tool used in ComposedMCPServer. This should not happen.")
return fmt.Errorf("DescriptiveTool.Call should not be invoked on a ComposedMCPServer's tool")
}
// Description returns the tool's description.
func (dt *DescriptiveTool) Description() string {
return dt.description
}
// InputSchema returns the tool's input schema.
func (dt *DescriptiveTool) InputSchema() map[string]any {
return dt.inputSchema
}
// OutputSchema returns the tool's output schema (MCP Protocol Version 2025-06-18).
func (dt *DescriptiveTool) OutputSchema() map[string]any {
return dt.outputSchema
}