@@ -2176,3 +2176,176 @@ func GetIssueDependencies(ctx context.Context, client *github.Client, cache *loc
21762176
21772177 return utils .NewToolResultText (string (r )), nil
21782178}
2179+
2180+ // DependencyWrite creates a tool to manage dependencies between issues.
2181+ func DependencyWrite (t translations.TranslationHelperFunc ) inventory.ServerTool {
2182+ return NewTool (
2183+ ToolsetMetadataIssues ,
2184+ mcp.Tool {
2185+ Name : "dependency_write" ,
2186+ Description : t ("TOOL_DEPENDENCY_WRITE_DESCRIPTION" , "Add or remove dependency relationships between issues in a GitHub repository." ),
2187+ Annotations : & mcp.ToolAnnotations {
2188+ Title : t ("TOOL_DEPENDENCY_WRITE_USER_TITLE" , "Manage issue dependencies" ),
2189+ ReadOnlyHint : false ,
2190+ },
2191+ InputSchema : & jsonschema.Schema {
2192+ Type : "object" ,
2193+ Properties : map [string ]* jsonschema.Schema {
2194+ "method" : {
2195+ Type : "string" ,
2196+ Description : `The action to perform on issue dependencies.
2197+ Options are:
2198+ - 'add' - add a dependency relationship (mark an issue as blocked by another issue).
2199+ - 'remove' - remove a dependency relationship.
2200+ ` ,
2201+ Enum : []any {"add" , "remove" },
2202+ },
2203+ "owner" : {
2204+ Type : "string" ,
2205+ Description : "Repository owner" ,
2206+ },
2207+ "repo" : {
2208+ Type : "string" ,
2209+ Description : "Repository name" ,
2210+ },
2211+ "issue_number" : {
2212+ Type : "number" ,
2213+ Description : "The number of the issue" ,
2214+ },
2215+ "dependency_issue_number" : {
2216+ Type : "number" ,
2217+ Description : "The number of the issue that blocks this issue (for 'add') or the dependency ID (for 'remove')" ,
2218+ },
2219+ },
2220+ Required : []string {"method" , "owner" , "repo" , "issue_number" , "dependency_issue_number" },
2221+ },
2222+ },
2223+ []scopes.Scope {scopes .Repo },
2224+ func (ctx context.Context , deps ToolDependencies , _ * mcp.CallToolRequest , args map [string ]any ) (* mcp.CallToolResult , any , error ) {
2225+ method , err := RequiredParam [string ](args , "method" )
2226+ if err != nil {
2227+ return utils .NewToolResultError (err .Error ()), nil , nil
2228+ }
2229+
2230+ owner , err := RequiredParam [string ](args , "owner" )
2231+ if err != nil {
2232+ return utils .NewToolResultError (err .Error ()), nil , nil
2233+ }
2234+
2235+ repo , err := RequiredParam [string ](args , "repo" )
2236+ if err != nil {
2237+ return utils .NewToolResultError (err .Error ()), nil , nil
2238+ }
2239+
2240+ issueNumber , err := RequiredInt (args , "issue_number" )
2241+ if err != nil {
2242+ return utils .NewToolResultError (err .Error ()), nil , nil
2243+ }
2244+
2245+ dependencyIssueNumber , err := RequiredInt (args , "dependency_issue_number" )
2246+ if err != nil {
2247+ return utils .NewToolResultError (err .Error ()), nil , nil
2248+ }
2249+
2250+ client , err := deps .GetClient (ctx )
2251+ if err != nil {
2252+ return utils .NewToolResultErrorFromErr ("failed to get GitHub client" , err ), nil , nil
2253+ }
2254+
2255+ switch method {
2256+ case "add" :
2257+ result , err := AddIssueDependency (ctx , client , owner , repo , issueNumber , dependencyIssueNumber )
2258+ return result , nil , err
2259+ case "remove" :
2260+ result , err := RemoveIssueDependency (ctx , client , owner , repo , issueNumber , dependencyIssueNumber )
2261+ return result , nil , err
2262+ default :
2263+ return utils .NewToolResultError (fmt .Sprintf ("unknown method: %s" , method )), nil , nil
2264+ }
2265+ })
2266+ }
2267+
2268+ // AddIssueDependency adds a dependency relationship between two issues.
2269+ // The issue specified by issueNumber will be blocked by the issue specified by dependencyIssueNumber.
2270+ func AddIssueDependency (ctx context.Context , client * github.Client , owner string , repo string , issueNumber int , dependencyIssueNumber int ) (* mcp.CallToolResult , error ) {
2271+ url := fmt .Sprintf ("repos/%s/%s/issues/%d/dependencies" , owner , repo , issueNumber )
2272+
2273+ body := map [string ]interface {}{
2274+ "dependency_issue_id" : dependencyIssueNumber ,
2275+ }
2276+
2277+ req , err := client .NewRequest ("POST" , url , body )
2278+ if err != nil {
2279+ return nil , fmt .Errorf ("failed to create request: %w" , err )
2280+ }
2281+
2282+ var dependency IssueDependency
2283+ resp , err := client .Do (ctx , req , & dependency )
2284+ if err != nil {
2285+ return ghErrors .NewGitHubAPIErrorResponse (ctx ,
2286+ "failed to add issue dependency" ,
2287+ resp ,
2288+ err ,
2289+ ), nil
2290+ }
2291+
2292+ defer func () { _ = resp .Body .Close () }()
2293+
2294+ if resp .StatusCode != http .StatusCreated && resp .StatusCode != http .StatusOK {
2295+ body , err := io .ReadAll (resp .Body )
2296+ if err != nil {
2297+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
2298+ }
2299+ return ghErrors .NewGitHubAPIStatusErrorResponse (ctx , "failed to add issue dependency" , resp , body ), nil
2300+ }
2301+
2302+ r , err := json .Marshal (map [string ]interface {}{
2303+ "success" : true ,
2304+ "message" : fmt .Sprintf ("Issue #%d is now blocked by issue #%d" , issueNumber , dependencyIssueNumber ),
2305+ "dependency" : dependency ,
2306+ })
2307+ if err != nil {
2308+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
2309+ }
2310+
2311+ return utils .NewToolResultText (string (r )), nil
2312+ }
2313+
2314+ // RemoveIssueDependency removes a dependency relationship between two issues.
2315+ func RemoveIssueDependency (ctx context.Context , client * github.Client , owner string , repo string , issueNumber int , dependencyID int ) (* mcp.CallToolResult , error ) {
2316+ url := fmt .Sprintf ("repos/%s/%s/issues/%d/dependencies/%d" , owner , repo , issueNumber , dependencyID )
2317+
2318+ req , err := client .NewRequest ("DELETE" , url , nil )
2319+ if err != nil {
2320+ return nil , fmt .Errorf ("failed to create request: %w" , err )
2321+ }
2322+
2323+ resp , err := client .Do (ctx , req , nil )
2324+ if err != nil {
2325+ return ghErrors .NewGitHubAPIErrorResponse (ctx ,
2326+ "failed to remove issue dependency" ,
2327+ resp ,
2328+ err ,
2329+ ), nil
2330+ }
2331+
2332+ defer func () { _ = resp .Body .Close () }()
2333+
2334+ if resp .StatusCode != http .StatusNoContent && resp .StatusCode != http .StatusOK {
2335+ body , err := io .ReadAll (resp .Body )
2336+ if err != nil {
2337+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
2338+ }
2339+ return ghErrors .NewGitHubAPIStatusErrorResponse (ctx , "failed to remove issue dependency" , resp , body ), nil
2340+ }
2341+
2342+ r , err := json .Marshal (map [string ]interface {}{
2343+ "success" : true ,
2344+ "message" : fmt .Sprintf ("Dependency removed from issue #%d" , issueNumber ),
2345+ })
2346+ if err != nil {
2347+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
2348+ }
2349+
2350+ return utils .NewToolResultText (string (r )), nil
2351+ }
0 commit comments