- Added initialization for LLM Provider and MCP Client Registries during the Load process. - Implemented reload functionality for both registries to ensure they are properly refreshed when needed. - Enhanced error handling to capture and report issues during initialization and reloading of the registries.
136 lines
2.8 KiB
Go
136 lines
2.8 KiB
Go
package mcpclient
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/yaoapp/gou/mcp"
|
|
mcpTypes "github.com/yaoapp/gou/mcp/types"
|
|
)
|
|
|
|
// runtimeID builds the runtime ID for registering into mcp.clients.
|
|
// Dynamic clients get an owner prefix to avoid collision with builtin IDs.
|
|
func runtimeID(c *Client) string {
|
|
switch c.Owner.Type {
|
|
case "user":
|
|
return "u" + c.Owner.ID + "." + c.ID
|
|
case "team":
|
|
return "t" + c.Owner.ID + "." + c.ID
|
|
case "assistant":
|
|
return "a" + c.Owner.ID + "." + c.ID
|
|
default:
|
|
return "s." + c.ID
|
|
}
|
|
}
|
|
|
|
// ensureClient makes sure the MCP client is registered in the runtime.
|
|
// Builtin clients are managed by engine.Load and skipped here.
|
|
func ensureClient(c *Client) error {
|
|
if c.Source == ClientSourceBuiltIn {
|
|
return nil
|
|
}
|
|
if !c.Enabled {
|
|
return nil
|
|
}
|
|
|
|
rid := c.RuntimeID
|
|
if rid == "" {
|
|
rid = runtimeID(c)
|
|
}
|
|
|
|
if mcp.Exists(rid) {
|
|
return nil
|
|
}
|
|
|
|
dslJSON, err := json.Marshal(c.ClientDSL)
|
|
if err != nil {
|
|
return fmt.Errorf("ensureClient %s: marshal DSL: %w", c.ID, err)
|
|
}
|
|
|
|
clientType := c.ClientDSL.Type
|
|
_, err = mcp.LoadClientSourceWithType(string(dslJSON), rid, clientType)
|
|
if err != nil {
|
|
return fmt.Errorf("ensureClient %s: LoadClientSourceWithType: %w", c.ID, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// unloadClient removes the client from the runtime.
|
|
func unloadClient(c *Client) {
|
|
if c.Source == ClientSourceBuiltIn {
|
|
return
|
|
}
|
|
rid := c.RuntimeID
|
|
if rid == "" {
|
|
rid = runtimeID(c)
|
|
}
|
|
mcp.UnloadClient(rid)
|
|
}
|
|
|
|
// importFromClients scans existing MCP clients loaded by engine.Load
|
|
// and imports them as builtin entries into the Registry store.
|
|
// If a store record with the same ID already exists (dynamic), it is not overwritten.
|
|
func importFromClients(r *Registry) error {
|
|
ids := mcp.ListClients()
|
|
for _, id := range ids {
|
|
if r.store.Has(storeKey(id)) {
|
|
continue
|
|
}
|
|
|
|
cl := clientFromRuntime(id)
|
|
if cl == nil {
|
|
continue
|
|
}
|
|
|
|
m, err := clientToMap(cl)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
sk := storeKey(id)
|
|
_ = r.store.Set(sk, m, 0)
|
|
if r.cache != nil {
|
|
_ = r.cache.Set(sk, m, 0)
|
|
}
|
|
_ = indexAdd(r.store, r.cache, id)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// clientFromRuntime builds a Client from a runtime mcp.Client interface.
|
|
// Uses Info() and GetMetaInfo() since full ClientDSL is not exposed.
|
|
func clientFromRuntime(id string) *Client {
|
|
defer func() { recover() }()
|
|
|
|
mcpClient := mcp.GetClient(id)
|
|
if mcpClient == nil {
|
|
return nil
|
|
}
|
|
|
|
info := mcpClient.Info()
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
|
|
meta := mcpClient.GetMetaInfo()
|
|
|
|
name := info.Name
|
|
if name == "" {
|
|
name = id
|
|
}
|
|
|
|
return &Client{
|
|
ClientDSL: mcpTypes.ClientDSL{
|
|
ID: id,
|
|
Name: name,
|
|
Type: info.Type,
|
|
Transport: info.Transport,
|
|
MetaInfo: meta,
|
|
},
|
|
RuntimeID: id,
|
|
Enabled: true,
|
|
Status: "connected",
|
|
Source: ClientSourceBuiltIn,
|
|
Owner: ClientOwner{Type: "system"},
|
|
}
|
|
}
|