- capability/mcp 整包 17 个文件核查:仅 config.go / registry.go 含英文注释(38 行)→ 全部中文化; 其余 15 个文件本就全中文,零改动。 - 保留 YAML/Go/JSON 示例行与字符串字面量(非注释,禁止改动)。 - 验收:机械校验为空;gofmt -l 干净;go build/vet/test ./internal/capability/mcp/... 全绿。
118 lines
2.9 KiB
Go
118 lines
2.9 KiB
Go
package mcp
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"gopkg.in/yaml.v3"
|
||
)
|
||
|
||
// Load 从进程环境读取 MCP server 配置,返回所有已配置的服务器;
|
||
// 由调用方按 Enabled 过滤。
|
||
func Load() (*Config, error) {
|
||
return FromEnv(os.Environ())
|
||
}
|
||
|
||
// FromEnv 从形如 os.Environ() 或测试夹具的 KEY=value 键值对切片构建 Config。
|
||
// 带编号的服务器按索引从 MCP_<NAME>_* 读取;不带编号的 MCP_* 变量描述
|
||
// 单台默认服务器。逐组读取(每组一台服务器),扫描在遇到第一组
|
||
// NAME/COMMAND/URL 全空的配置时停止。
|
||
func FromEnv(pairs []string) (*Config, error) {
|
||
env := make(map[string]string, len(pairs))
|
||
for _, p := range pairs {
|
||
k, v, ok := strings.Cut(p, "=")
|
||
if ok {
|
||
env[k] = v
|
||
}
|
||
}
|
||
return &Config{Servers: fromEnvMap(env)}, nil
|
||
}
|
||
|
||
// FromYAML 解析描述 MCP server 的 YAML 文档,例如:
|
||
//
|
||
// servers:
|
||
// - name: calc
|
||
// command: mcp-calc
|
||
// args: [--stdio]
|
||
// enabled: true
|
||
func FromYAML(data []byte) (*Config, error) {
|
||
var doc struct {
|
||
Servers []MCPConfig `yaml:"servers"`
|
||
}
|
||
if err := yaml.Unmarshal(data, &doc); err != nil {
|
||
return nil, fmt.Errorf("mcp config: %w", err)
|
||
}
|
||
return &Config{Servers: doc.Servers}, nil
|
||
}
|
||
|
||
// fromEnvMap 从已解析的环境变量 map 构建服务器列表。
|
||
func fromEnvMap(env map[string]string) []MCPConfig {
|
||
var servers []MCPConfig
|
||
if cfg, ok := readOne(env, "MCP"); ok {
|
||
servers = append(servers, cfg)
|
||
}
|
||
for i := 0; ; i++ {
|
||
cfg, ok := readOne(env, "MCP_"+strconv.Itoa(i))
|
||
if !ok {
|
||
break
|
||
}
|
||
servers = append(servers, cfg)
|
||
}
|
||
return servers
|
||
}
|
||
|
||
// readOne 用给定的变量前缀(如 "MCP" 或 "MCP_0")从环境读取一台 MCPConfig;
|
||
// 没有任何标识字段时返回 ok=false。
|
||
func readOne(env map[string]string, prefix string) (MCPConfig, bool) {
|
||
p := prefix
|
||
if p == "" {
|
||
p = "MCP"
|
||
}
|
||
name := env[p+"_NAME"]
|
||
command := env[p+"_COMMAND"]
|
||
url := env[p+"_URL"]
|
||
transp := env[p+"_TRANSPORT"]
|
||
if name == "" && command == "" && url == "" {
|
||
return MCPConfig{}, false
|
||
}
|
||
return MCPConfig{
|
||
Name: name,
|
||
Command: command,
|
||
Args: splitArgs(env[p+"_ARGS"]),
|
||
URL: url,
|
||
TransportType: transp,
|
||
Enabled: parseBool(env[p+"_ENABLED"], false),
|
||
}, true
|
||
}
|
||
|
||
// splitArgs 拆分逗号分隔的 args 值,去掉空白与空项。
|
||
func splitArgs(raw string) []string {
|
||
raw = strings.TrimSpace(raw)
|
||
if raw == "" {
|
||
return nil
|
||
}
|
||
parts := strings.Split(raw, ",")
|
||
out := make([]string, 0, len(parts))
|
||
for _, p := range parts {
|
||
if t := strings.TrimSpace(p); t != "" {
|
||
out = append(out, t)
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
// parseBool 解析布尔值;输入为空或非法时返回默认值 def。
|
||
func parseBool(raw string, def bool) bool {
|
||
raw = strings.TrimSpace(raw)
|
||
if raw == "" {
|
||
return def
|
||
}
|
||
b, err := strconv.ParseBool(strings.ToLower(raw))
|
||
if err != nil {
|
||
return def
|
||
}
|
||
return b
|
||
}
|