Add additional tools

This commit is contained in:
Colin McDonnell
2025-11-20 00:34:03 -08:00
parent 098df15764
commit 85f8fbfaf5
7 changed files with 185 additions and 15 deletions
-3
View File
@@ -44,9 +44,6 @@ Do not try to handle github auth- treat ${ghPullfrogMcpName} as a black box that
When using ${ghPullfrogMcpName}, use the tools to comment and interact in a way that a real member of the team would.
Ensure after your edits are done, your final comments do not contain intermediate reasoning or context, e.g. "I'll respond to the question."
For CI failures:
- Use ${ghPullfrogMcpName}/get_check_suite_logs with check_suite.id to get all failed workflow logs
## Mode Selection
Before starting any work, you must first determine which mode to use by examining the request and calling ${ghPullfrogMcpName}/select_mode.
+47
View File
@@ -27,6 +27,53 @@ await mcp.call("gh-pullfrog/get_check_suite_logs", {
});
```
### review tools
#### `get_review_comments`
get all line-by-line comments for a specific pull request review.
**parameters:**
- `pull_number` (number): the pull request number
- `review_id` (number): the id from review.id in the webhook payload
**replaces:** `gh api repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments`
**returns:**
array of review comments including:
- file path, line number, comment body
- side (LEFT/RIGHT) and position in diff
- user, timestamps, html_url
- in_reply_to_id for threaded comments
**example:**
```typescript
// when handling a pull_request_review_submitted webhook
await mcp.call("gh-pullfrog/get_review_comments", {
pull_number: 47,
review_id: review.id
});
```
#### `list_pull_request_reviews`
list all reviews for a pull request.
**parameters:**
- `pull_number` (number): the pull request number
**replaces:** `gh api repos/{owner}/{repo}/pulls/{pull_number}/reviews`
**returns:**
array of reviews with:
- review id, body, state (approved/changes_requested/commented)
- user, commit_id, submitted_at, html_url
**example:**
```typescript
await mcp.call("gh-pullfrog/list_pull_request_reviews", {
pull_number: 47
});
```
### other tools
see individual files for documentation on other tools:
+12 -11
View File
@@ -12,17 +12,18 @@ export const GetCheckSuiteLogsTool = tool({
parameters: GetCheckSuiteLogs,
execute: contextualize(async ({ check_suite_id }, ctx) => {
// get workflow runs for this specific check suite
const runsResponse = await ctx.octokit.rest.actions.listWorkflowRunsForRepo({
owner: ctx.owner,
repo: ctx.name,
check_suite_id,
per_page: 100,
});
const failedRuns = runsResponse.data.workflow_runs.filter(
(run) => run.conclusion === "failure"
const workflowRuns = await ctx.octokit.paginate(
ctx.octokit.rest.actions.listWorkflowRunsForRepo,
{
owner: ctx.owner,
repo: ctx.name,
check_suite_id,
per_page: 100,
}
);
const failedRuns = workflowRuns.filter((run) => run.conclusion === "failure");
if (failedRuns.length === 0) {
return {
check_suite_id,
@@ -34,14 +35,14 @@ export const GetCheckSuiteLogsTool = tool({
// get logs for each failed run
const logsForRuns = await Promise.all(
failedRuns.map(async (run) => {
const jobsResponse = await ctx.octokit.rest.actions.listJobsForWorkflowRun({
const jobs = await ctx.octokit.paginate(ctx.octokit.rest.actions.listJobsForWorkflowRun, {
owner: ctx.owner,
repo: ctx.name,
run_id: run.id,
});
const jobLogs = await Promise.all(
jobsResponse.data.jobs.map(async (job) => {
jobs.map(async (job) => {
try {
const logsResponse = await ctx.octokit.rest.actions.downloadJobLogsForWorkflowRun({
owner: ctx.owner,
+43
View File
@@ -0,0 +1,43 @@
import { execSync } from "node:child_process";
import { type } from "arktype";
import { contextualize, tool } from "./shared.ts";
export const ListFiles = type({
path: type.string
.describe("The path to list files from (defaults to current directory)")
.default("."),
});
export const ListFilesTool = tool({
name: "list_files",
description:
"List files in the repository using git ls-files. Useful for discovering the file structure and locating files.",
parameters: ListFiles,
execute: contextualize(async ({ path }) => {
try {
// Use git ls-files to list tracked files
// This respects .gitignore and gives a clean list of source files
const command = path === "." ? "git ls-files" : `git ls-files ${path}`;
const output = execSync(command, { encoding: "utf-8" });
const files = output.split("\n").filter((f) => f.trim() !== "");
if (files.length === 0) {
// Fallback for non-git environments or untracked files
const findCmd = `find ${path} -maxdepth 3 -not -path '*/.*' -type f`;
const findOutput = execSync(findCmd, { encoding: "utf-8" });
return {
files: findOutput.split("\n").filter((f) => f.trim() !== ""),
method: "find",
};
}
return { files, method: "git" };
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
error: `Failed to list files: ${errorMessage}`,
hint: "Try using a specific path if the repository root is not the current directory.",
};
}
}),
});
+2 -1
View File
@@ -36,7 +36,8 @@ export const PullRequestInfoTool = tool({
execSync(`git fetch origin ${headBranch}`, { stdio: "inherit" });
log.info(`Checking out PR branch: origin/${headBranch}`);
execSync(`git checkout origin/${headBranch}`, { stdio: "inherit" });
// check out a local branch tracking the remote branch so we can push changes
execSync(`git checkout -B ${headBranch} origin/${headBranch}`, { stdio: "inherit" });
return {
number: data.number,
+76
View File
@@ -0,0 +1,76 @@
import { type } from "arktype";
import { contextualize, tool } from "./shared.ts";
export const GetReviewComments = type({
pull_number: type.number.describe("The pull request number"),
review_id: type.number.describe("The review ID to get comments for"),
});
export const GetReviewCommentsTool = tool({
name: "get_review_comments",
description:
"Get all review comments for a specific pull request review. Returns line-by-line comments that were left on specific code locations.",
parameters: GetReviewComments,
execute: contextualize(async ({ pull_number, review_id }, ctx) => {
const comments = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listCommentsForReview, {
owner: ctx.owner,
repo: ctx.name,
pull_number,
review_id,
});
return {
review_id,
pull_number,
comments: comments.map((comment) => ({
id: comment.id,
body: comment.body,
path: comment.path,
line: comment.line,
side: comment.side,
start_line: comment.start_line,
start_side: comment.start_side,
user: typeof comment.user === "string" ? comment.user : comment.user?.login,
created_at: comment.created_at,
updated_at: comment.updated_at,
html_url: comment.html_url,
in_reply_to_id: comment.in_reply_to_id,
diff_hunk: comment.diff_hunk,
reactions: comment.reactions,
})),
count: comments.length,
};
}),
});
export const ListPullRequestReviews = type({
pull_number: type.number.describe("The pull request number to list reviews for"),
});
export const ListPullRequestReviewsTool = tool({
name: "list_pull_request_reviews",
description:
"List all reviews for a pull request. Returns all reviews including approvals, request changes, and comments.",
parameters: ListPullRequestReviews,
execute: contextualize(async ({ pull_number }, ctx) => {
const reviews = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listReviews, {
owner: ctx.owner,
repo: ctx.name,
pull_number,
});
return {
pull_number,
reviews: reviews.map((review) => ({
id: review.id,
body: review.body,
state: review.state,
user: review.user?.login,
commit_id: review.commit_id,
submitted_at: review.submitted_at,
html_url: review.html_url,
})),
count: reviews.length,
};
}),
});
+5
View File
@@ -6,10 +6,12 @@ import {
EditCommentTool,
UpdateWorkingCommentTool,
} from "./comment.ts";
// import { ListFilesTool } from "./files.ts";
import { IssueTool } from "./issue.ts";
import { PullRequestTool } from "./pr.ts";
import { PullRequestInfoTool } from "./prInfo.ts";
import { ReviewTool } from "./review.ts";
import { GetReviewCommentsTool, ListPullRequestReviewsTool } from "./reviewComments.ts";
import { SelectModeTool } from "./selectMode.ts";
import { addTools } from "./shared.ts";
@@ -25,9 +27,12 @@ addTools(server, [
CreateWorkingCommentTool,
UpdateWorkingCommentTool,
IssueTool,
// ListFilesTool,
PullRequestTool,
ReviewTool,
PullRequestInfoTool,
GetReviewCommentsTool,
ListPullRequestReviewsTool,
GetCheckSuiteLogsTool,
]);