Files
Cyrene_v2/cmd/cyrene/mcp.go
T
AskaEth 52f08dc343
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
docs(cyrene): cmd/cyrene 存量英文注释中文化(22 个非测试文件,仅注释)
- 四组并行完成: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 全过)。
2026-10-04 16:58:06 +08:00

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
}