You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
+366
-12
@@ -24,6 +24,11 @@ func registerPMTools(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
registerGetIssue(srv, deps)
|
||||
registerGetRequirement(srv, deps)
|
||||
registerCreateIssue(srv, deps)
|
||||
registerCreateSubtask(srv, deps)
|
||||
registerAddDependency(srv, deps)
|
||||
registerRemoveDependency(srv, deps)
|
||||
registerListDependencies(srv, deps)
|
||||
registerSubmitDecomposition(srv, deps)
|
||||
registerUpdateIssue(srv, deps)
|
||||
registerCloseIssue(srv, deps)
|
||||
registerReopenIssue(srv, deps)
|
||||
@@ -34,6 +39,7 @@ func registerPMTools(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
registerReopenRequirement(srv, deps)
|
||||
registerSetRequirementPhase(srv, deps)
|
||||
registerAddRequirementArtifact(srv, deps)
|
||||
registerApproveRequirementArtifact(srv, deps)
|
||||
registerListRequirementArtifacts(srv, deps)
|
||||
}
|
||||
|
||||
@@ -380,6 +386,312 @@ func registerCreateIssue(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
})
|
||||
}
|
||||
|
||||
// ===== create_subtask =====
|
||||
|
||||
type createSubtaskIn struct {
|
||||
ProjectSlug string `json:"project_slug" jsonschema:"required field"`
|
||||
ParentNumber int `json:"parent_number" jsonschema:"parent issue number (>=1)"`
|
||||
Title string `json:"title" jsonschema:"title (non-empty)"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Priority int `json:"priority,omitempty" jsonschema:"scheduling priority 0..3 (higher = sooner)"`
|
||||
}
|
||||
|
||||
func registerCreateSubtask(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "create_subtask", Description: "Create a subtask under a parent issue (task decomposition). Sets parent_id + priority."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in createSubtaskIn) (*mcpsdk.CallToolResult, issueCreateOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "create_subtask"); err != nil {
|
||||
return nil, issueCreateOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, issueCreateOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Get(ctx, c, in.ProjectSlug)
|
||||
if err != nil {
|
||||
return nil, issueCreateOut{}, err
|
||||
}
|
||||
if err := CheckProjectFromCtx(ctx, p.ID); err != nil {
|
||||
return nil, issueCreateOut{}, err
|
||||
}
|
||||
prio := in.Priority
|
||||
i, err := deps.Issues.CreateSubtask(ctx, c, in.ProjectSlug, in.ParentNumber, project.CreateIssueInput{
|
||||
Title: in.Title, Description: in.Description, Priority: &prio,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, issueCreateOut{}, err
|
||||
}
|
||||
return nil, issueCreateOut{OK: true, Issue: issueDetail{
|
||||
Number: i.Number, Title: i.Title, Description: i.Description,
|
||||
Status: string(i.Status), CreatedAt: i.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
}}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ===== add_dependency =====
|
||||
|
||||
type addDependencyIn struct {
|
||||
ProjectSlug string `json:"project_slug" jsonschema:"required field"`
|
||||
BlockedNumber int `json:"blocked_number" jsonschema:"issue blocked-by blocker_number"`
|
||||
BlockerNumber int `json:"blocker_number" jsonschema:"blocking issue number"`
|
||||
}
|
||||
type dependencyOut struct {
|
||||
OK bool `json:"ok"`
|
||||
BlockedNumber int `json:"blocked_number"`
|
||||
BlockerNumber int `json:"blocker_number"`
|
||||
}
|
||||
|
||||
func registerAddDependency(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "add_dependency", Description: "Add a dependency edge: blocked_number is blocked-by blocker_number. Rejects cycles and cross-project edges."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in addDependencyIn) (*mcpsdk.CallToolResult, dependencyOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "add_dependency"); err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Get(ctx, c, in.ProjectSlug)
|
||||
if err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
if err := CheckProjectFromCtx(ctx, p.ID); err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
if _, err := deps.Issues.AddDependency(ctx, c, in.ProjectSlug, project.AddDependencyInput{
|
||||
BlockedNumber: in.BlockedNumber, BlockerNumber: in.BlockerNumber,
|
||||
}); err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
return nil, dependencyOut{OK: true, BlockedNumber: in.BlockedNumber, BlockerNumber: in.BlockerNumber}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ===== remove_dependency =====
|
||||
|
||||
func registerRemoveDependency(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "remove_dependency", Description: "Remove a dependency edge (plan correction)."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in addDependencyIn) (*mcpsdk.CallToolResult, dependencyOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "remove_dependency"); err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Get(ctx, c, in.ProjectSlug)
|
||||
if err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
if err := CheckProjectFromCtx(ctx, p.ID); err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
if err := deps.Issues.RemoveDependency(ctx, c, in.ProjectSlug, in.BlockedNumber, in.BlockerNumber); err != nil {
|
||||
return nil, dependencyOut{}, err
|
||||
}
|
||||
return nil, dependencyOut{OK: true, BlockedNumber: in.BlockedNumber, BlockerNumber: in.BlockerNumber}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ===== list_dependencies =====
|
||||
|
||||
type listDependenciesIn struct {
|
||||
ProjectSlug string `json:"project_slug" jsonschema:"required field"`
|
||||
Number int `json:"number" jsonschema:"issue number (>=1)"`
|
||||
}
|
||||
type listDependenciesOut struct {
|
||||
BlockedBy []issueSummary `json:"blocked_by"`
|
||||
Blocks []issueSummary `json:"blocks"`
|
||||
}
|
||||
|
||||
func registerListDependencies(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "list_dependencies", Description: "List an issue's dependencies: {blocked_by:[...], blocks:[...]}."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in listDependenciesIn) (*mcpsdk.CallToolResult, listDependenciesOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "list_dependencies"); err != nil {
|
||||
return nil, listDependenciesOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, listDependenciesOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Get(ctx, c, in.ProjectSlug)
|
||||
if err != nil {
|
||||
return nil, listDependenciesOut{}, err
|
||||
}
|
||||
if err := CheckProjectFromCtx(ctx, p.ID); err != nil {
|
||||
return nil, listDependenciesOut{}, err
|
||||
}
|
||||
blockedBy, blocks, err := deps.Issues.ListDependencies(ctx, c, in.ProjectSlug, in.Number)
|
||||
if err != nil {
|
||||
return nil, listDependenciesOut{}, err
|
||||
}
|
||||
out := listDependenciesOut{
|
||||
BlockedBy: make([]issueSummary, 0, len(blockedBy)),
|
||||
Blocks: make([]issueSummary, 0, len(blocks)),
|
||||
}
|
||||
for _, i := range blockedBy {
|
||||
out.BlockedBy = append(out.BlockedBy, issueSummary{Number: i.Number, Title: i.Title, Status: string(i.Status)})
|
||||
}
|
||||
for _, i := range blocks {
|
||||
out.Blocks = append(out.Blocks, issueSummary{Number: i.Number, Title: i.Title, Status: string(i.Status)})
|
||||
}
|
||||
return nil, out, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ===== submit_decomposition =====
|
||||
//
|
||||
// 一次原子调用提交整张任务分解图(子任务 + 依赖边)。先做 in-batch DAG 校验,全部
|
||||
// 通过后再建子任务、建边。ref 为 batch 内本地引用键,落库后映射为 issue number。
|
||||
|
||||
type decompSubtaskIn struct {
|
||||
Ref string `json:"ref" jsonschema:"local reference key used by edges"`
|
||||
Title string `json:"title" jsonschema:"title (non-empty)"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Priority int `json:"priority,omitempty" jsonschema:"0..3 (higher = sooner)"`
|
||||
}
|
||||
type decompEdgeIn struct {
|
||||
BlockedRef string `json:"blocked_ref" jsonschema:"ref of the blocked subtask"`
|
||||
BlockerRef string `json:"blocker_ref" jsonschema:"ref of the blocking subtask"`
|
||||
}
|
||||
type submitDecompositionIn struct {
|
||||
ProjectSlug string `json:"project_slug" jsonschema:"required field"`
|
||||
ParentNumber *int `json:"parent_number,omitempty" jsonschema:"optional anchor issue number; subtasks become its children"`
|
||||
Subtasks []decompSubtaskIn `json:"subtasks"`
|
||||
Edges []decompEdgeIn `json:"edges,omitempty"`
|
||||
}
|
||||
type submitDecompositionOut struct {
|
||||
OK bool `json:"ok"`
|
||||
RefToNumber map[string]int `json:"ref_to_number"`
|
||||
}
|
||||
|
||||
func registerSubmitDecomposition(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "submit_decomposition", Description: "Submit a whole task decomposition (subtasks + blocks/blocked-by edges) atomically. Validates acyclicity before any write; maps refs to created issue numbers."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in submitDecompositionIn) (*mcpsdk.CallToolResult, submitDecompositionOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "submit_decomposition"); err != nil {
|
||||
return nil, submitDecompositionOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, submitDecompositionOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Get(ctx, c, in.ProjectSlug)
|
||||
if err != nil {
|
||||
return nil, submitDecompositionOut{}, err
|
||||
}
|
||||
if err := CheckProjectFromCtx(ctx, p.ID); err != nil {
|
||||
return nil, submitDecompositionOut{}, err
|
||||
}
|
||||
res, err := applyDecomposition(ctx, deps.Issues, c, in)
|
||||
if err != nil {
|
||||
return nil, submitDecompositionOut{}, err
|
||||
}
|
||||
return nil, submitDecompositionOut{OK: true, RefToNumber: res}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// applyDecomposition 在 mcp 包内实现分解落库(不引入 orchestrator 以避免 import cycle)。
|
||||
// 先 ref 去重 + 边端点校验 + DAG 校验,再建子任务、建边。
|
||||
func applyDecomposition(ctx context.Context, issues project.IssueService, c project.Caller, in submitDecompositionIn) (map[string]int, error) {
|
||||
if len(in.Subtasks) == 0 {
|
||||
return nil, errs.New(errs.CodeInvalidInput, "decomposition 至少需要一个子任务")
|
||||
}
|
||||
refSet := make(map[string]struct{}, len(in.Subtasks))
|
||||
for _, st := range in.Subtasks {
|
||||
if st.Ref == "" {
|
||||
return nil, errs.New(errs.CodeInvalidInput, "子任务 ref 不能为空")
|
||||
}
|
||||
if st.Title == "" {
|
||||
return nil, errs.New(errs.CodeInvalidInput, "子任务 title 不能为空")
|
||||
}
|
||||
if _, dup := refSet[st.Ref]; dup {
|
||||
return nil, errs.New(errs.CodeInvalidInput, "子任务 ref 重复: "+st.Ref)
|
||||
}
|
||||
if st.Priority < 0 || st.Priority > 3 {
|
||||
return nil, errs.New(errs.CodeInvalidInput, "子任务 priority 必须在 0..3")
|
||||
}
|
||||
refSet[st.Ref] = struct{}{}
|
||||
}
|
||||
for _, e := range in.Edges {
|
||||
if _, ok := refSet[e.BlockedRef]; !ok {
|
||||
return nil, errs.New(errs.CodeInvalidInput, "边引用了未知 blocked ref: "+e.BlockedRef)
|
||||
}
|
||||
if _, ok := refSet[e.BlockerRef]; !ok {
|
||||
return nil, errs.New(errs.CodeInvalidInput, "边引用了未知 blocker ref: "+e.BlockerRef)
|
||||
}
|
||||
if e.BlockedRef == e.BlockerRef {
|
||||
return nil, errs.New(errs.CodeInvalidInput, "边不能自指: "+e.BlockedRef)
|
||||
}
|
||||
}
|
||||
if err := decompositionIsDAG(in.Subtasks, in.Edges); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refToNum := make(map[string]int, len(in.Subtasks))
|
||||
for _, st := range in.Subtasks {
|
||||
prio := st.Priority
|
||||
ci := project.CreateIssueInput{Title: st.Title, Description: st.Description, Priority: &prio}
|
||||
var iss *project.Issue
|
||||
var err error
|
||||
if in.ParentNumber != nil {
|
||||
iss, err = issues.CreateSubtask(ctx, c, in.ProjectSlug, *in.ParentNumber, ci)
|
||||
} else {
|
||||
iss, err = issues.Create(ctx, c, in.ProjectSlug, ci)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refToNum[st.Ref] = iss.Number
|
||||
}
|
||||
for _, e := range in.Edges {
|
||||
if _, err := issues.AddDependency(ctx, c, in.ProjectSlug, project.AddDependencyInput{
|
||||
BlockedNumber: refToNum[e.BlockedRef], BlockerNumber: refToNum[e.BlockerRef],
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return refToNum, nil
|
||||
}
|
||||
|
||||
// decompositionIsDAG 对 (子任务, 边) 做 Kahn 拓扑排序,存在环返回 CodeInvalidInput。
|
||||
func decompositionIsDAG(subtasks []decompSubtaskIn, edges []decompEdgeIn) error {
|
||||
indeg := make(map[string]int, len(subtasks))
|
||||
for _, st := range subtasks {
|
||||
indeg[st.Ref] = 0
|
||||
}
|
||||
adj := make(map[string][]string, len(subtasks))
|
||||
for _, e := range edges {
|
||||
adj[e.BlockerRef] = append(adj[e.BlockerRef], e.BlockedRef)
|
||||
indeg[e.BlockedRef]++
|
||||
}
|
||||
queue := make([]string, 0, len(subtasks))
|
||||
for ref, d := range indeg {
|
||||
if d == 0 {
|
||||
queue = append(queue, ref)
|
||||
}
|
||||
}
|
||||
visited := 0
|
||||
for len(queue) > 0 {
|
||||
cur := queue[0]
|
||||
queue = queue[1:]
|
||||
visited++
|
||||
for _, next := range adj[cur] {
|
||||
indeg[next]--
|
||||
if indeg[next] == 0 {
|
||||
queue = append(queue, next)
|
||||
}
|
||||
}
|
||||
}
|
||||
if visited != len(subtasks) {
|
||||
return errs.New(errs.CodeInvalidInput, "decomposition 边集合存在环")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ===== update_issue =====
|
||||
|
||||
type updateIssueIn struct {
|
||||
@@ -687,7 +999,7 @@ type setRequirementPhaseIn struct {
|
||||
|
||||
func registerSetRequirementPhase(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "set_requirement_phase", Description: "Change a requirement's phase."},
|
||||
&mcpsdk.Tool{Name: "set_requirement_phase", Description: "Change a requirement's phase. Entry gates are enforced: e.g. moving to 'implementing' requires an approved (verdict=pass) auditing artifact; moving to 'done' requires the workspace PR to be merged. A blocked transition returns phase_gate_failed."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in setRequirementPhaseIn) (*mcpsdk.CallToolResult, requirementCreateOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "set_requirement_phase"); err != nil {
|
||||
return nil, requirementCreateOut{}, err
|
||||
@@ -720,7 +1032,7 @@ func registerSetRequirementPhase(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
type addRequirementArtifactIn struct {
|
||||
ProjectSlug string `json:"project_slug" jsonschema:"required field"`
|
||||
Number int `json:"number" jsonschema:"requirement number (>=1)"`
|
||||
Phase string `json:"phase" jsonschema:"artifact phase: planning|prototyping|auditing"`
|
||||
Phase string `json:"phase" jsonschema:"artifact phase: planning|prototyping|auditing|implementing|reviewing"`
|
||||
Content string `json:"content" jsonschema:"artifact content (non-empty)"`
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
@@ -730,7 +1042,19 @@ type artifactDetail struct {
|
||||
Content string `json:"content"`
|
||||
Note string `json:"note,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Verdict string `json:"verdict"`
|
||||
Approved bool `json:"approved"`
|
||||
}
|
||||
|
||||
func artifactDetailFrom(a *project.Artifact) artifactDetail {
|
||||
return artifactDetail{
|
||||
Phase: string(a.Phase), Version: a.Version, Content: a.Content, Note: a.Note,
|
||||
CreatedAt: a.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
Verdict: string(a.Verdict),
|
||||
Approved: a.Verdict == project.VerdictPass,
|
||||
}
|
||||
}
|
||||
|
||||
type artifactCreateOut struct {
|
||||
OK bool `json:"ok"`
|
||||
Artifact artifactDetail `json:"artifact"`
|
||||
@@ -738,7 +1062,7 @@ type artifactCreateOut struct {
|
||||
|
||||
func registerAddRequirementArtifact(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "add_requirement_artifact", Description: "Add a new phase artifact version (planning/prototyping/auditing) to a requirement."},
|
||||
&mcpsdk.Tool{Name: "add_requirement_artifact", Description: "Add a new phase artifact version (planning/prototyping/auditing/implementing/reviewing) to a requirement."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in addRequirementArtifactIn) (*mcpsdk.CallToolResult, artifactCreateOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "add_requirement_artifact"); err != nil {
|
||||
return nil, artifactCreateOut{}, err
|
||||
@@ -760,10 +1084,43 @@ func registerAddRequirementArtifact(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
if err != nil {
|
||||
return nil, artifactCreateOut{}, err
|
||||
}
|
||||
return nil, artifactCreateOut{OK: true, Artifact: artifactDetail{
|
||||
Phase: string(art.Phase), Version: art.Version, Content: art.Content, Note: art.Note,
|
||||
CreatedAt: art.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
}}, nil
|
||||
return nil, artifactCreateOut{OK: true, Artifact: artifactDetailFrom(art)}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ===== approve_requirement_artifact =====
|
||||
|
||||
type approveRequirementArtifactIn struct {
|
||||
ProjectSlug string `json:"project_slug" jsonschema:"required field"`
|
||||
Number int `json:"number" jsonschema:"requirement number (>=1)"`
|
||||
Phase string `json:"phase" jsonschema:"artifact phase: planning|prototyping|auditing|implementing|reviewing"`
|
||||
Version int `json:"version" jsonschema:"artifact version (>=1)"`
|
||||
Verdict string `json:"verdict" jsonschema:"review verdict: pass|fail"`
|
||||
}
|
||||
|
||||
func registerApproveRequirementArtifact(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "approve_requirement_artifact", Description: "Set the review verdict (pass/fail) on a requirement artifact version. A 'pass' verdict marks the phase's approved/current artifact, which downstream phase gates require (e.g. an approved auditing artifact unblocks the move to 'implementing')."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in approveRequirementArtifactIn) (*mcpsdk.CallToolResult, artifactCreateOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "approve_requirement_artifact"); err != nil {
|
||||
return nil, artifactCreateOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, artifactCreateOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Get(ctx, c, in.ProjectSlug)
|
||||
if err != nil {
|
||||
return nil, artifactCreateOut{}, err
|
||||
}
|
||||
if err := CheckProjectFromCtx(ctx, p.ID); err != nil {
|
||||
return nil, artifactCreateOut{}, err
|
||||
}
|
||||
art, err := deps.Artifacts.Approve(ctx, c, in.ProjectSlug, in.Number, project.Phase(in.Phase), in.Version, project.Verdict(in.Verdict))
|
||||
if err != nil {
|
||||
return nil, artifactCreateOut{}, err
|
||||
}
|
||||
return nil, artifactCreateOut{OK: true, Artifact: artifactDetailFrom(art)}, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -772,7 +1129,7 @@ func registerAddRequirementArtifact(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
type listRequirementArtifactsIn struct {
|
||||
ProjectSlug string `json:"project_slug" jsonschema:"required field"`
|
||||
Number int `json:"number" jsonschema:"requirement number (>=1)"`
|
||||
Phase string `json:"phase,omitempty" jsonschema:"filter by phase: planning|prototyping|auditing; empty = all"`
|
||||
Phase string `json:"phase,omitempty" jsonschema:"filter by phase: planning|prototyping|auditing|implementing|reviewing; empty = all"`
|
||||
}
|
||||
type listRequirementArtifactsOut struct {
|
||||
Artifacts []artifactDetail `json:"artifacts"`
|
||||
@@ -802,10 +1159,7 @@ func registerListRequirementArtifacts(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
}
|
||||
out := listRequirementArtifactsOut{Artifacts: make([]artifactDetail, 0, len(arts))}
|
||||
for _, art := range arts {
|
||||
out.Artifacts = append(out.Artifacts, artifactDetail{
|
||||
Phase: string(art.Phase), Version: art.Version, Content: art.Content, Note: art.Note,
|
||||
CreatedAt: art.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
})
|
||||
out.Artifacts = append(out.Artifacts, artifactDetailFrom(art))
|
||||
}
|
||||
return nil, out, nil
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user