// mcp_inject.go 构造 session/new 下发的 mcpServers 列表。 // // 组成:ACW 自家 MCP(http 类型,per-session system token 做 Bearer 认证)+ // AgentKind 上配置的第三方 MCP。能力门控(ACP v1):stdio 是基线直接下发; // http / sse 需 agent 在 initialize 响应中声明 mcpCapabilities.http / .sse, // 未声明的条目逐条跳过并记日志——session 照常启动,自家 MCP 仍可由管理员 // 通过配置文件 + ACW_MCP_TOKEN 环境变量引用做静态接入兜底。 package acp import ( "log/slog" "sort" "github.com/google/uuid" "github.com/yan1h/agent-coding-workflow/internal/acp/handlers" ) // buildMcpServers 按 agent 能力构造 mcpServers。acwURL / acwToken 任一为空时 // 不注入自家 MCP(如 MCP 模块未配置 public_url)。 func buildMcpServers(specs []McpServerSpec, acwURL, acwToken string, httpOK, sseOK bool, log *slog.Logger, sessionID uuid.UUID) []any { out := []any{} if acwURL != "" && acwToken != "" { if httpOK { out = append(out, handlers.McpServerHTTP{ Type: "http", Name: AcwMcpServerName, URL: acwURL, Headers: []handlers.HttpHeader{{Name: "Authorization", Value: "Bearer " + acwToken}}, }) } else { log.Warn("acp.mcp_inject.acw_skipped_no_http_capability", "session_id", sessionID, "hint", "agent 未声明 mcpCapabilities.http;可在 AgentKind 配置文件中静态引用 ACW_MCP_TOKEN 接入") } } for _, sp := range specs { switch sp.Type { case McpStdio: args := sp.Args if args == nil { args = []string{} } out = append(out, handlers.McpServerStdio{ Name: sp.Name, Command: sp.Command, Args: args, Env: toEnvVariables(sp.Env), }) case McpHTTP: if !httpOK { log.Warn("acp.mcp_inject.server_skipped_no_capability", "session_id", sessionID, "name", sp.Name, "type", "http") continue } out = append(out, handlers.McpServerHTTP{ Type: "http", Name: sp.Name, URL: sp.URL, Headers: toHTTPHeaders(sp.Headers), }) case McpSSE: if !sseOK { log.Warn("acp.mcp_inject.server_skipped_no_capability", "session_id", sessionID, "name", sp.Name, "type", "sse") continue } out = append(out, handlers.McpServerHTTP{ Type: "sse", Name: sp.Name, URL: sp.URL, Headers: toHTTPHeaders(sp.Headers), }) } } return out } // toEnvVariables / toHTTPHeaders 把 map 转为 ACP wire 的 name-value 数组, // 按 key 排序保证序列化确定性(map 迭代随机)。 func toEnvVariables(m map[string]string) []handlers.EnvVariable { out := make([]handlers.EnvVariable, 0, len(m)) for _, k := range sortedKeys(m) { out = append(out, handlers.EnvVariable{Name: k, Value: m[k]}) } return out } func toHTTPHeaders(m map[string]string) []handlers.HttpHeader { out := make([]handlers.HttpHeader, 0, len(m)) for _, k := range sortedKeys(m) { out = append(out, handlers.HttpHeader{Name: k, Value: m[k]}) } return out } func sortedKeys(m map[string]string) []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } sort.Strings(keys) return keys }