mirror of
https://github.com/alibaba/higress.git
synced 2026-02-28 22:50:57 +08:00
128 lines
4.6 KiB
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
|
|
}
|