// mcp_bridge.go 实现 mcp.ACPBridge:把 mcp 的 acp-free DTO 转译到本包 // service 调用。放在 acp 包内(acp 已 import mcp,转译逻辑贴近领域类型), // 避免 mcp → acp 的循环依赖。 package acp import ( "context" "github.com/google/uuid" "github.com/yan1h/agent-coding-workflow/internal/mcp" ) // MCPBridge 适配 AgentKind/Session/Permission 三个服务到 mcp.ACPBridge。 type MCPBridge struct { kinds AgentKindService sessions SessionService perms *PermissionService } // NewMCPBridge 构造桥接适配器,由 app.go 装配 mcp.ServerDeps 时调用。 func NewMCPBridge(kinds AgentKindService, sessions SessionService, perms *PermissionService) *MCPBridge { return &MCPBridge{kinds: kinds, sessions: sessions, perms: perms} } var _ mcp.ACPBridge = (*MCPBridge)(nil) func (b *MCPBridge) ListAgentKinds(ctx context.Context, userID uuid.UUID, isAdmin bool) ([]mcp.ACPAgentKind, error) { ks, err := b.kinds.List(ctx, Caller{UserID: userID, IsAdmin: isAdmin}) if err != nil { return nil, err } out := make([]mcp.ACPAgentKind, 0, len(ks)) for _, k := range ks { out = append(out, agentKindToMCP(k)) } return out, nil } func (b *MCPBridge) GetAgentKind(ctx context.Context, userID uuid.UUID, isAdmin bool, id uuid.UUID) (*mcp.ACPAgentKind, error) { k, err := b.kinds.Get(ctx, Caller{UserID: userID, IsAdmin: isAdmin}, id) if err != nil { return nil, err } out := agentKindToMCP(k) return &out, nil } func (b *MCPBridge) CreateSession(ctx context.Context, userID uuid.UUID, isAdmin bool, in mcp.ACPCreateSessionInput) (*mcp.ACPSession, error) { s, err := b.sessions.Create(ctx, Caller{UserID: userID, IsAdmin: isAdmin}, CreateSessionInput{ WorkspaceID: in.WorkspaceID, AgentKindID: in.AgentKindID, Branch: in.Branch, IssueNumber: in.IssueNumber, RequirementNumber: in.RequirementNumber, InitialPrompt: in.InitialPrompt, }) if err != nil { return nil, err } out := sessionToMCP(s) return &out, nil } func (b *MCPBridge) GetSession(ctx context.Context, userID uuid.UUID, isAdmin bool, id uuid.UUID) (*mcp.ACPSession, error) { s, err := b.sessions.Get(ctx, Caller{UserID: userID, IsAdmin: isAdmin}, id) if err != nil { return nil, err } out := sessionToMCP(s) return &out, nil } func (b *MCPBridge) ListSessions(ctx context.Context, userID uuid.UUID, isAdmin bool, f mcp.ACPSessionFilter) ([]mcp.ACPSession, error) { ss, err := b.sessions.List(ctx, Caller{UserID: userID, IsAdmin: isAdmin}, SessionListFilter{ All: f.All, RequirementID: f.RequirementID, }) if err != nil { return nil, err } out := make([]mcp.ACPSession, 0, len(ss)) for _, s := range ss { out = append(out, sessionToMCP(s)) } return out, nil } func (b *MCPBridge) TerminateSession(ctx context.Context, userID uuid.UUID, isAdmin bool, id uuid.UUID) error { return b.sessions.Terminate(ctx, Caller{UserID: userID, IsAdmin: isAdmin}, id) } func (b *MCPBridge) ListPendingPermissions(ctx context.Context, userID uuid.UUID, isAdmin bool, sessionID uuid.UUID) ([]mcp.ACPPermission, error) { ps, err := b.perms.ListPendingBySession(ctx, Caller{UserID: userID, IsAdmin: isAdmin}, sessionID) if err != nil { return nil, err } out := make([]mcp.ACPPermission, 0, len(ps)) for _, p := range ps { out = append(out, mcp.ACPPermission{ ID: p.ID, SessionID: p.SessionID, AgentRequestID: p.AgentRequestID, ToolName: p.ToolName, Status: string(p.Status), CreatedAt: p.CreatedAt, }) } return out, nil } func agentKindToMCP(k *AgentKind) mcp.ACPAgentKind { return mcp.ACPAgentKind{ ID: k.ID, Name: k.Name, DisplayName: k.DisplayName, Description: k.Description, Enabled: k.Enabled, ToolAllowlist: k.ToolAllowlist, } } func sessionToMCP(s *Session) mcp.ACPSession { return mcp.ACPSession{ ID: s.ID, WorkspaceID: s.WorkspaceID, ProjectID: s.ProjectID, AgentKindID: s.AgentKindID, UserID: s.UserID, IssueID: s.IssueID, RequirementID: s.RequirementID, Branch: s.Branch, IsMainWorktree: s.IsMainWorktree, Status: string(s.Status), LastError: s.LastError, StartedAt: s.StartedAt, EndedAt: s.EndedAt, } }