From c6c1210fa046e1fa594894f1134174536c613303 Mon Sep 17 00:00:00 2001 From: David Blass Date: Thu, 23 Oct 2025 15:21:08 -0400 Subject: [PATCH] refactor tool implementation --- agents/claude.ts | 9 +++++- mcp/comment.ts | 56 ++++++++++------------------------- mcp/issue.ts | 70 +++++++++++++------------------------------- mcp/pr.ts | 76 ++++++++++++++++-------------------------------- mcp/shared.ts | 46 +++++++++++++++++++++++++++++ package.json | 2 +- 6 files changed, 116 insertions(+), 143 deletions(-) diff --git a/agents/claude.ts b/agents/claude.ts index aef5a64..64eb102 100644 --- a/agents/claude.ts +++ b/agents/claude.ts @@ -165,8 +165,15 @@ export class ClaudeAgent implements Agent { */ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void { try { + // Skip debug lines that start with [DEBUG] or [debug] + const trimmedChunk = chunk.trim(); + if (trimmedChunk.startsWith("[DEBUG]") || trimmedChunk.startsWith("[debug]")) { + console.log(chunk); + return; + } + console.log(chunk); - const parsedChunk = JSON.parse(chunk.trim()); + const parsedChunk = JSON.parse(trimmedChunk); switch (parsedChunk.type) { case "system": diff --git a/mcp/comment.ts b/mcp/comment.ts index 0d73af3..6286361 100644 --- a/mcp/comment.ts +++ b/mcp/comment.ts @@ -1,5 +1,5 @@ import { type } from "arktype"; -import { getMcpContext, tool } from "./shared.ts"; +import { contextualize, tool } from "./shared.ts"; export const Comment = type({ issueNumber: type.number.describe("the issue number to comment on"), @@ -10,45 +10,19 @@ export const CommentTool = tool({ name: "create_issue_comment", description: "Create a comment on a GitHub issue", parameters: Comment, - execute: async ({ issueNumber, body }) => { - const ctx = getMcpContext(); - try { - const result = await ctx.octokit.rest.issues.createComment({ - owner: ctx.owner, - repo: ctx.name, - issue_number: issueNumber, - body: body, - }); + execute: contextualize(async ({ issueNumber, body }, ctx) => { + const result = await ctx.octokit.rest.issues.createComment({ + owner: ctx.owner, + repo: ctx.name, + issue_number: issueNumber, + body: body, + }); - return { - content: [ - { - type: "text", - text: JSON.stringify( - { - success: true, - commentId: result.data.id, - url: result.data.html_url, - body: result.data.body, - }, - null, - 2 - ), - }, - ], - }; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - return { - content: [ - { - type: "text", - text: `Error creating comment: ${errorMessage}`, - }, - ], - error: errorMessage, - isError: true, - }; - } - }, + return { + success: true, + commentId: result.data.id, + url: result.data.html_url, + body: result.data.body, + }; + }), }); diff --git a/mcp/issue.ts b/mcp/issue.ts index 20a108d..133be05 100644 --- a/mcp/issue.ts +++ b/mcp/issue.ts @@ -1,5 +1,5 @@ import { type } from "arktype"; -import { getMcpContext, tool } from "./shared.ts"; +import { contextualize, tool } from "./shared.ts"; export const Issue = type({ title: type.string.describe("the title of the issue"), @@ -18,53 +18,25 @@ export const IssueTool = tool({ name: "create_issue", description: "Create a new GitHub issue", parameters: Issue, - execute: async ({ title, body, labels, assignees }) => { - const ctx = getMcpContext(); - try { - const result = await ctx.octokit.rest.issues.create({ - owner: ctx.owner, - repo: ctx.name, - title: title, - body: body, - labels: labels ?? [], - assignees: assignees ?? [], - }); + execute: contextualize(async ({ title, body, labels, assignees }, ctx) => { + const result = await ctx.octokit.rest.issues.create({ + owner: ctx.owner, + repo: ctx.name, + title: title, + body: body, + labels: labels ?? [], + assignees: assignees ?? [], + }); - return { - content: [ - { - type: "text", - text: JSON.stringify( - { - success: true, - issueId: result.data.id, - number: result.data.number, - url: result.data.html_url, - title: result.data.title, - state: result.data.state, - labels: result.data.labels?.map((label) => - typeof label === "string" ? label : label.name - ), - assignees: result.data.assignees?.map((assignee) => assignee.login), - }, - null, - 2 - ), - }, - ], - }; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - return { - content: [ - { - type: "text", - text: `Error creating issue: ${errorMessage}`, - }, - ], - error: errorMessage, - isError: true, - }; - } - }, + return { + success: true, + issueId: result.data.id, + number: result.data.number, + url: result.data.html_url, + title: result.data.title, + state: result.data.state, + labels: result.data.labels?.map((label) => (typeof label === "string" ? label : label.name)), + assignees: result.data.assignees?.map((assignee) => assignee.login), + }; + }), }); diff --git a/mcp/pr.ts b/mcp/pr.ts index 7eba331..0887926 100644 --- a/mcp/pr.ts +++ b/mcp/pr.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import { type } from "arktype"; -import { getMcpContext, tool } from "./shared.ts"; +import { contextualize, tool } from "./shared.ts"; export const PullRequest = type({ title: type.string.describe("the title of the pull request"), @@ -12,57 +12,31 @@ export const PullRequestTool = tool({ name: "create_pull_request", description: "Create a pull request from the current branch", parameters: PullRequest, - execute: async ({ title, body, base }) => { - const ctx = getMcpContext(); - try { - // Get the current branch name - const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - }).trim(); + execute: contextualize(async ({ title, body, base }, ctx) => { + // Get the current branch name + const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + }).trim(); - console.log(`Current branch: ${currentBranch}`); + console.log(`Current branch: ${currentBranch}`); - const result = await ctx.octokit.rest.pulls.create({ - owner: ctx.owner, - repo: ctx.name, - title: title, - body: body, - head: currentBranch, - base: base, - }); + const result = await ctx.octokit.rest.pulls.create({ + owner: ctx.owner, + repo: ctx.name, + title: title, + body: body, + head: currentBranch, + base: base, + }); - return { - content: [ - { - type: "text", - text: JSON.stringify( - { - success: true, - pullRequestId: result.data.id, - number: result.data.number, - url: result.data.html_url, - title: result.data.title, - head: result.data.head.ref, - base: result.data.base.ref, - }, - null, - 2 - ), - }, - ], - }; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - return { - content: [ - { - type: "text", - text: `Error creating pull request: ${errorMessage}`, - }, - ], - error: errorMessage, - isError: true, - }; - } - }, + return { + success: true, + pullRequestId: result.data.id, + number: result.data.number, + url: result.data.html_url, + title: result.data.title, + head: result.data.head.ref, + base: result.data.base.ref, + }; + }), }); diff --git a/mcp/shared.ts b/mcp/shared.ts index ee1210e..e454ed2 100644 --- a/mcp/shared.ts +++ b/mcp/shared.ts @@ -4,6 +4,15 @@ import type { StandardSchemaV1 } from "@standard-schema/spec"; import type { FastMCP, Tool } from "fastmcp"; import { parseRepoContext, type RepoContext } from "../utils/github.ts"; +export interface ToolResult { + content: { + type: "text"; + text: string; + }[]; + error?: string; + isError?: boolean; +} + export const getMcpContext = cached((): McpContext => { const githubInstallationToken = process.env.GITHUB_INSTALLATION_TOKEN; if (!githubInstallationToken) { @@ -30,3 +39,40 @@ export const addTools = (server: FastMCP, tools: Tool[]) => { } return server; }; + +export const contextualize = + (executor: (params: T, ctx: McpContext) => Promise>) => + async (params: T): Promise => { + try { + const ctx = getMcpContext(); + const result = await executor(params, ctx); + return handleToolSuccess(result); + } catch (error) { + return handleToolError(error); + } + }; + +const handleToolSuccess = (data: Record): ToolResult => { + return { + content: [ + { + type: "text", + text: JSON.stringify(data, null, 2), + }, + ], + }; +}; + +const handleToolError = (error: unknown): ToolResult => { + const errorMessage = error instanceof Error ? error.message : String(error); + return { + content: [ + { + type: "text", + text: `Error: ${errorMessage}`, + }, + ], + error: errorMessage, + isError: true, + }; +}; diff --git a/package.json b/package.json index 09a0cd3..72e5a06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pullfrog/action", - "version": "0.0.53", + "version": "0.0.54", "type": "module", "files": [ "index.js",