ACP / MCP

This commit is contained in:
2026-06-12 11:44:19 +08:00
parent 2a9661a6ef
commit 4ea4adfd8e
47 changed files with 2558 additions and 240 deletions
+94 -2
View File
@@ -29,10 +29,21 @@ func (s *agentKindService) Create(ctx context.Context, c Caller, in CreateAgentK
if in.Name == "" || in.DisplayName == "" || in.BinaryPath == "" {
return nil, errs.New(errs.CodeInvalidInput, "name / display_name / binary_path required")
}
clientType := in.ClientType
if clientType == "" {
clientType = ClientGeneric
}
if !clientType.Valid() {
return nil, errs.New(errs.CodeInvalidInput, "invalid client_type")
}
encrypted, err := encryptEnv(s.enc, in.Env)
if err != nil {
return nil, err
}
encryptedMCP, err := encryptMCPServers(s.enc, in.MCPServers)
if err != nil {
return nil, err
}
args := in.Args
if args == nil {
args = []string{}
@@ -44,8 +55,10 @@ func (s *agentKindService) Create(ctx context.Context, c Caller, in CreateAgentK
k := &AgentKind{
ID: uuid.New(), Name: in.Name, DisplayName: in.DisplayName, Description: in.Description,
BinaryPath: in.BinaryPath, Args: args, EncryptedEnv: encrypted, Enabled: in.Enabled,
ToolAllowlist: allowlist,
CreatedBy: c.UserID,
ToolAllowlist: allowlist,
ClientType: clientType,
EncryptedMCPServers: encryptedMCP,
CreatedBy: c.UserID,
}
out, err := s.repo.CreateAgentKind(ctx, k)
if err != nil {
@@ -114,6 +127,21 @@ func (s *agentKindService) Update(ctx context.Context, c Caller, id uuid.UUID, i
cur.ToolAllowlist = in.ToolAllowlist
changed["tool_allowlist"] = "<changed>"
}
if in.ClientType != nil && *in.ClientType != cur.ClientType {
if !in.ClientType.Valid() {
return nil, errs.New(errs.CodeInvalidInput, "invalid client_type")
}
cur.ClientType = *in.ClientType
changed["client_type"] = string(*in.ClientType)
}
if in.MCPServers != nil {
encryptedMCP, eerr := encryptMCPServers(s.enc, in.MCPServers)
if eerr != nil {
return nil, eerr
}
cur.EncryptedMCPServers = encryptedMCP
changed["mcp_servers"] = "<changed>"
}
if in.Enabled != nil && *in.Enabled != cur.Enabled {
cur.Enabled = *in.Enabled
changed["enabled"] = *in.Enabled
@@ -171,6 +199,70 @@ func encryptEnv(enc *crypto.Encryptor, env map[string]string) ([]byte, error) {
return enc.Encrypt(b)
}
// encryptMCPServers 校验并加密 MCP server 列表。nil → nil(不存储)。
func encryptMCPServers(enc *crypto.Encryptor, servers []McpServerSpec) ([]byte, error) {
if servers == nil {
return nil, nil
}
if err := validateMCPServers(servers); err != nil {
return nil, err
}
b, err := json.Marshal(servers)
if err != nil {
return nil, errs.Wrap(err, errs.CodeInternal, "marshal mcp servers")
}
return enc.Encrypt(b)
}
// decryptMCPServers 解密 MCP server 列表。空密文 → 空列表。
func decryptMCPServers(enc *crypto.Encryptor, encrypted []byte) ([]McpServerSpec, error) {
if len(encrypted) == 0 {
return []McpServerSpec{}, nil
}
plain, err := enc.Decrypt(encrypted)
if err != nil {
return nil, errs.Wrap(err, errs.CodeInternal, "decrypt mcp servers")
}
var out []McpServerSpec
if err := json.Unmarshal(plain, &out); err != nil {
return nil, errs.Wrap(err, errs.CodeInternal, "unmarshal mcp servers")
}
if out == nil {
out = []McpServerSpec{}
}
return out, nil
}
// validateMCPServers 校验条目:name 必填且不重复;stdio 需 command,http/sse 需 url。
func validateMCPServers(servers []McpServerSpec) error {
seen := map[string]bool{}
for _, sv := range servers {
if sv.Name == "" {
return errs.New(errs.CodeInvalidInput, "mcp server name required")
}
if sv.Name == AcwMcpServerName {
return errs.New(errs.CodeInvalidInput, "mcp server name '"+AcwMcpServerName+"' is reserved")
}
if seen[sv.Name] {
return errs.New(errs.CodeInvalidInput, "duplicate mcp server name: "+sv.Name)
}
seen[sv.Name] = true
switch sv.Type {
case McpStdio:
if sv.Command == "" {
return errs.New(errs.CodeInvalidInput, "mcp server "+sv.Name+": command required for stdio")
}
case McpHTTP, McpSSE:
if sv.URL == "" {
return errs.New(errs.CodeInvalidInput, "mcp server "+sv.Name+": url required for "+string(sv.Type))
}
default:
return errs.New(errs.CodeInvalidInput, "mcp server "+sv.Name+": invalid type")
}
}
return nil
}
// decryptEnv 解密并反序列化 env map。Part 2 的 supervisor.Spawn 用。
func decryptEnv(enc *crypto.Encryptor, encrypted []byte) (map[string]string, error) {
if len(encrypted) == 0 {