@@ -153,6 +153,118 @@ func AddIssueComment(getClient GetClientFn, t translations.TranslationHelperFunc
153
153
}
154
154
}
155
155
156
+ // AddSubIssue creates a tool to add a sub-issue to a parent issue.
157
+ func AddSubIssue (getClient GetClientFn ,t translations.TranslationHelperFunc ) (tool mcp.Tool ,handler server.ToolHandlerFunc ) {
158
+ return mcp .NewTool ("add_sub_issue" ,
159
+ mcp .WithDescription (t ("TOOL_ADD_SUB_ISSUE_DESCRIPTION" ,"Add a sub-issue to a parent issue in a GitHub repository." )),
160
+ mcp .WithToolAnnotation (mcp.ToolAnnotation {
161
+ Title :t ("TOOL_ADD_SUB_ISSUE_USER_TITLE" ,"Add sub-issue" ),
162
+ ReadOnlyHint :toBoolPtr (false ),
163
+ }),
164
+ mcp .WithString ("owner" ,
165
+ mcp .Required (),
166
+ mcp .Description ("Repository owner" ),
167
+ ),
168
+ mcp .WithString ("repo" ,
169
+ mcp .Required (),
170
+ mcp .Description ("Repository name" ),
171
+ ),
172
+ mcp .WithNumber ("issue_number" ,
173
+ mcp .Required (),
174
+ mcp .Description ("The number of the parent issue" ),
175
+ ),
176
+ mcp .WithNumber ("sub_issue_id" ,
177
+ mcp .Required (),
178
+ mcp .Description ("The ID of the sub-issue to add" ),
179
+ ),
180
+ mcp .WithBoolean ("replace_parent" ,
181
+ mcp .Description ("When true, replaces the sub-issue's current parent issue" ),
182
+ ),
183
+ ),
184
+ func (ctx context.Context ,request mcp.CallToolRequest ) (* mcp.CallToolResult ,error ) {
185
+ owner ,err := requiredParam [string ](request ,"owner" )
186
+ if err != nil {
187
+ return mcp .NewToolResultError (err .Error ()),nil
188
+ }
189
+ repo ,err := requiredParam [string ](request ,"repo" )
190
+ if err != nil {
191
+ return mcp .NewToolResultError (err .Error ()),nil
192
+ }
193
+ issueNumber ,err := RequiredInt (request ,"issue_number" )
194
+ if err != nil {
195
+ return mcp .NewToolResultError (err .Error ()),nil
196
+ }
197
+ subIssueID ,err := RequiredInt (request ,"sub_issue_id" )
198
+ if err != nil {
199
+ return mcp .NewToolResultError (err .Error ()),nil
200
+ }
201
+ replaceParent ,err := OptionalParam [bool ](request ,"replace_parent" )
202
+ if err != nil {
203
+ return mcp .NewToolResultError (err .Error ()),nil
204
+ }
205
+
206
+ client ,err := getClient (ctx )
207
+ if err != nil {
208
+ return nil ,fmt .Errorf ("failed to get GitHub client: %w" ,err )
209
+ }
210
+
211
+ // Create the request body
212
+ requestBody := map [string ]interface {}{
213
+ "sub_issue_id" :subIssueID ,
214
+ }
215
+ if replaceParent {
216
+ requestBody ["replace_parent" ]= replaceParent
217
+ }
218
+
219
+ // Since the go-github library might not have sub-issues support yet,
220
+ // we'll make a direct HTTP request using the client's HTTP client
221
+ reqBodyBytes ,err := json .Marshal (requestBody )
222
+ if err != nil {
223
+ return nil ,fmt .Errorf ("failed to marshal request body: %w" ,err )
224
+ }
225
+
226
+ url := fmt .Sprintf ("https://api.github.com/repos/%s/%s/issues/%d/sub_issues" ,owner ,repo ,issueNumber )
227
+ req ,err := http .NewRequestWithContext (ctx ,"POST" ,url ,strings .NewReader (string (reqBodyBytes )))
228
+ if err != nil {
229
+ return nil ,fmt .Errorf ("failed to create request: %w" ,err )
230
+ }
231
+
232
+ req .Header .Set ("Accept" ,"application/vnd.github+json" )
233
+ req .Header .Set ("Content-Type" ,"application/json" )
234
+ req .Header .Set ("X-GitHub-Api-Version" ,"2022-11-28" )
235
+
236
+ // Use the same authentication as the GitHub client
237
+ httpClient := client .Client ()
238
+ resp ,err := httpClient .Do (req )
239
+ if err != nil {
240
+ return nil ,fmt .Errorf ("failed to add sub-issue: %w" ,err )
241
+ }
242
+ defer func () {_ = resp .Body .Close () }()
243
+
244
+ body ,err := io .ReadAll (resp .Body )
245
+ if err != nil {
246
+ return nil ,fmt .Errorf ("failed to read response body: %w" ,err )
247
+ }
248
+
249
+ if resp .StatusCode != http .StatusCreated {
250
+ return mcp .NewToolResultError (fmt .Sprintf ("failed to add sub-issue: %s" ,string (body ))),nil
251
+ }
252
+
253
+ // Parse and re-marshal to ensure consistent formatting
254
+ var result interface {}
255
+ if err := json .Unmarshal (body ,& result );err != nil {
256
+ return nil ,fmt .Errorf ("failed to unmarshal response: %w" ,err )
257
+ }
258
+
259
+ r ,err := json .Marshal (result )
260
+ if err != nil {
261
+ return nil ,fmt .Errorf ("failed to marshal response: %w" ,err )
262
+ }
263
+
264
+ return mcp .NewToolResultText (string (r )),nil
265
+ }
266
+ }
267
+
156
268
// SearchIssues creates a tool to search for issues and pull requests.
157
269
func SearchIssues (getClient GetClientFn ,t translations.TranslationHelperFunc ) (tool mcp.Tool ,handler server.ToolHandlerFunc ) {
158
270
return mcp .NewTool ("search_issues" ,