function createListIssuesTool(config: GitHubConfig) {
return createTool({
name: "list_github_issues",
description: `List open issues in ${config.owner}/${config.repo}. Returns issue numbers, titles, labels, and assignees.`,
inputSchema: {
type: "object",
properties: {
state: {
type: "string",
enum: ["open", "closed", "all"],
description: "Issue state filter. Default: open.",
},
labels: {
type: "string",
description: "Comma-separated label names to filter by (e.g., 'bug,priority:high').",
},
limit: {
type: "number",
description: "Maximum issues to return. Default: 10, max: 100.",
},
},
},
execute: async (input) => {
const params = new URLSearchParams({
state: input.state ?? "open",
per_page: String(Math.min(input.limit ?? 10, 100)),
})
if (input.labels) params.set("labels", input.labels)
const response = await fetch(
`https://api.github.com/repos/${config.owner}/${config.repo}/issues?${params}`,
{ headers: { Authorization: `token ${config.token}` } }
)
const issues = await response.json()
return {
issues: issues.map((i: Record<string, unknown>) => ({
number: i.number,
title: i.title,
state: i.state,
labels: (i.labels as Array<{ name: string }>).map((l) => l.name),
assignee: (i.assignee as { login: string } | null)?.login,
createdAt: i.created_at,
})),
total: issues.length,
}
},
})
}
function createCreateIssueTool(config: GitHubConfig) {
return createTool({
name: "create_github_issue",
description: `Create a new issue in ${config.owner}/${config.repo}.`,
inputSchema: {
type: "object",
properties: {
title: { type: "string", description: "Issue title" },
body: { type: "string", description: "Issue body (Markdown supported)" },
labels: {
type: "array",
items: { type: "string" },
description: "Labels to apply",
},
},
required: ["title"],
},
execute: async (input) => {
const response = await fetch(
`https://api.github.com/repos/${config.owner}/${config.repo}/issues`,
{
method: "POST",
headers: {
Authorization: `token ${config.token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
title: input.title,
body: input.body,
labels: input.labels,
}),
}
)
const issue = await response.json()
return { number: issue.number, url: issue.html_url }
},
})
}
function createPostCommentTool(config: GitHubConfig) {
return createTool({
name: "post_github_comment",
description: `Post a comment on an issue or PR in ${config.owner}/${config.repo}.`,
inputSchema: {
type: "object",
properties: {
issueNumber: { type: "number", description: "Issue or PR number" },
body: { type: "string", description: "Comment body (Markdown supported)" },
},
required: ["issueNumber", "body"],
},
execute: async (input) => {
const response = await fetch(
`https://api.github.com/repos/${config.owner}/${config.repo}/issues/${input.issueNumber}/comments`,
{
method: "POST",
headers: {
Authorization: `token ${config.token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ body: input.body }),
}
)
const comment = await response.json()
return { id: comment.id, url: comment.html_url }
},
})
}