You've already forked agentic-coding-workflow
97 lines
3.0 KiB
Go
97 lines
3.0 KiB
Go
// 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
|
|
}
|