- 四组并行完成:main/wire*.go、wire_capacity|channels|runtime、channels|persona_*|emotion|observability、 voice|plugins|stream|skills|vision|subagent_tool|extractor|mcp|sessionread_tool|terminal_tool。 - 机械校验(git diff -U0 去 // 行与空行)仅剩 wire_frontend.go:84 一处行尾注释:代码前缀 「\ta.gw = gw 」经 cat -A 与 HEAD 逐字节比对一致(allow 清单已声明)。 - 验收:隔离 worktree(HEAD + 本批 patch)go build/vet 绿、全树 58 包 ok、 linux/amd64 + darwin/arm64 交叉编译绿;gofmt -l 干净。 - 备注:整树两次红均为负载敏感 flake(backthink / orchestration 的 timeout 类用例单独 -count=20 全过)。
86 lines
2.7 KiB
Go
86 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/capability/mcp"
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/capability/registry"
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/config"
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
|
|
)
|
|
|
|
// mcpConnectTimeout 约束每个 MCP server 的连接+发现握手,使挂死的 server 降级为
|
|
// 一条告警,而不是阻塞启动。
|
|
const mcpConnectTimeout = 15 * time.Second
|
|
|
|
// wireMCP 读取 cfg.MCP.ServersJSON,连接每个已启用的 server,把发现到的工具注册进
|
|
// 能力注册表(KindTool,使 agent 可调用它们),并暴露一个 KindMCP 能力,让管理面板
|
|
// 的插件列表能枚举该 server。连接失败或未配置的 server 降级为一条告警,绝不阻塞
|
|
// 启动。
|
|
func (a *app) wireMCP() {
|
|
log := logger.WithSource(a.log, "cmd/cyrene")
|
|
servers, err := a.mcpServers()
|
|
if err != nil {
|
|
log.Warn("mcp config parse skipped", "error", err)
|
|
return
|
|
}
|
|
if len(servers) == 0 {
|
|
log.Debug("no mcp servers configured")
|
|
return
|
|
}
|
|
for _, srv := range servers {
|
|
if !srv.Enabled {
|
|
continue
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), mcpConnectTimeout)
|
|
err := a.registerMCPServer(ctx, srv)
|
|
cancel()
|
|
if err != nil {
|
|
log.Warn("mcp server skipped", "name", srv.Name, "error", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// mcpServers 从 cfg.MCP.ServersJSON 解析出已配置的 MCP server。
|
|
func (a *app) mcpServers() ([]config.MCPServer, error) {
|
|
raw := strings.TrimSpace(a.cfg.MCP.ServersJSON)
|
|
if raw == "" {
|
|
return nil, nil
|
|
}
|
|
var servers []config.MCPServer
|
|
if err := json.Unmarshal([]byte(raw), &servers); err != nil {
|
|
return nil, err
|
|
}
|
|
return servers, nil
|
|
}
|
|
|
|
// registerMCPServer 连接单个 server,注册其工具,并以 "mcp:<name>" 发布一个
|
|
// KindMCP 能力。它返回第一个错误以便调用方分类;注册中途失败时会清理已加入的内容。
|
|
func (a *app) registerMCPServer(ctx context.Context, srv config.MCPServer) error {
|
|
log := logger.WithSource(a.log, "cmd/cyrene")
|
|
reg := mcp.NewRegistry(a.caps, mcp.MCPConfig{
|
|
Name: srv.Name,
|
|
Command: srv.Command,
|
|
Args: srv.Args,
|
|
URL: srv.URL,
|
|
}, mcp.WithLogger(a.log))
|
|
if err := reg.Register(ctx); err != nil {
|
|
_ = reg.Unregister(context.Background())
|
|
return err
|
|
}
|
|
if err := a.caps.Register(registry.Capability{
|
|
Name: "mcp:" + srv.Name,
|
|
Kind: registry.KindMCP,
|
|
Description: "MCP server " + srv.Name,
|
|
Value: reg,
|
|
}); err != nil {
|
|
log.Warn("mcp capability register skipped", "name", srv.Name, "error", err)
|
|
}
|
|
a.mcpRegs = append(a.mcpRegs, reg)
|
|
log.Info("mcp server registered", "name", srv.Name)
|
|
return nil
|
|
}
|