You've already forked agentic-coding-workflow
ACP / MCP
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user