From 61768339c109fb0f6ccf6dfd4379f70e6c7bec50 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 28 Jun 2026 04:36:10 +0000 Subject: [PATCH 1/5] feat(mcp): implement github.create_issue tool - Add github.create_issue tool definition to MCP_TOOLS in definitions.ts - Implement createIssueHandler in src/tools/github.create_issue.ts using td-cli - Wire up the handler in server.ts - Add unit tests for the new handler in src/tools/github.create_issue.test.ts --- boomtick-pkg/mcp/src/mcp/definitions.ts | 12 ++++ boomtick-pkg/mcp/src/mcp/server.ts | 3 + .../mcp/src/tools/github.create_issue.test.ts | 63 +++++++++++++++++++ .../mcp/src/tools/github.create_issue.ts | 43 +++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 boomtick-pkg/mcp/src/tools/github.create_issue.test.ts create mode 100644 boomtick-pkg/mcp/src/tools/github.create_issue.ts diff --git a/boomtick-pkg/mcp/src/mcp/definitions.ts b/boomtick-pkg/mcp/src/mcp/definitions.ts index 8bdfd79ee1..bd6989012f 100644 --- a/boomtick-pkg/mcp/src/mcp/definitions.ts +++ b/boomtick-pkg/mcp/src/mcp/definitions.ts @@ -341,6 +341,18 @@ export const MCP_TOOLS: Tool[] = [ required: ["issueNumber", "body"], }, }, + { + name: "github.create_issue", + description: "Create a new GitHub issue.", + inputSchema: { + type: "object", + properties: { + title: { type: "string", description: "The title of the issue." }, + body: { type: "string", description: "The body/description of the issue." }, + }, + required: ["title", "body"], + }, + }, { name: "jules.create_session", description: "Create a Jules session that performs work externally and may generate a GitHub pull request.", diff --git a/boomtick-pkg/mcp/src/mcp/server.ts b/boomtick-pkg/mcp/src/mcp/server.ts index a32f3894ca..e746245c8f 100644 --- a/boomtick-pkg/mcp/src/mcp/server.ts +++ b/boomtick-pkg/mcp/src/mcp/server.ts @@ -33,6 +33,7 @@ import { createPullRequestHandler, CreatePullRequestInputSchema } from "../tools import { issueViewHandler, IssueViewInputSchema } from "../tools/github.issue_view.js"; import { issueUpdateHandler, IssueUpdateInputSchema } from "../tools/github.issue_update.js"; import { issueCommentHandler, IssueCommentInputSchema } from "../tools/github.issue_comment.js"; +import { createIssueHandler, CreateIssueInputSchema } from "../tools/github.create_issue.js"; import { createJulesSessionHandler, CreateJulesSessionInputSchema } from "../tools/jules/create-session.js"; @@ -221,6 +222,8 @@ export class BoomtickMCPServer { return createSuccessResult(await issueUpdateHandler(IssueUpdateInputSchema.parse(request.params.arguments))); case "github.issue_comment": return createSuccessResult(await issueCommentHandler(IssueCommentInputSchema.parse(request.params.arguments))); + case "github.create_issue": + return createSuccessResult(await createIssueHandler(CreateIssueInputSchema.parse(request.params.arguments))); diff --git a/boomtick-pkg/mcp/src/tools/github.create_issue.test.ts b/boomtick-pkg/mcp/src/tools/github.create_issue.test.ts new file mode 100644 index 0000000000..cee72f496c --- /dev/null +++ b/boomtick-pkg/mcp/src/tools/github.create_issue.test.ts @@ -0,0 +1,63 @@ +import { describe, it, expect, vi } from "vitest"; +import { createIssueHandler } from "./github.create_issue.js"; +import * as shell from "../lib/shell.js"; + +vi.mock("../lib/shell.js", () => ({ + runCommand: vi.fn() +})); + +describe("github.create_issue", () => { + it("should create an issue successfully", async () => { + const mockResponse = { + status: "success", + issue: { + number: 123, + title: "Test Issue", + html_url: "https://github.com/owner/repo/issues/123", + state: "open" + } + }; + + vi.mocked(shell.runCommand).mockResolvedValue({ + stdout: JSON.stringify(mockResponse), + stderr: "", + exitCode: 0, + durationMs: 10, + command: "td-cli gh create-issue --title 'Test Issue' --body 'Test Body'" + }); + + const result = await createIssueHandler({ title: "Test Issue", body: "Test Body" }); + expect(result.status).toBe("success"); + expect(result.issue?.number).toBe(123); + expect(result.issue?.title).toBe("Test Issue"); + }); + + it("should throw error on command failure", async () => { + vi.mocked(shell.runCommand).mockResolvedValue({ + stdout: "", + stderr: "Auth failed", + exitCode: 1, + durationMs: 10, + command: "td-cli gh create-issue" + }); + + await expect(createIssueHandler({ title: "Test Issue", body: "Test Body" })).rejects.toThrow("Failed to create issue: Auth failed"); + }); + + it("should handle error status from CLI output", async () => { + const mockResponse = { + status: "error", + message: "Repo not found" + }; + + vi.mocked(shell.runCommand).mockResolvedValue({ + stdout: JSON.stringify(mockResponse), + stderr: "", + exitCode: 0, + durationMs: 10, + command: "td-cli gh create-issue" + }); + + await expect(createIssueHandler({ title: "Test Issue", body: "Test Body" })).rejects.toThrow("Failed to create issue: Repo not found"); + }); +}); diff --git a/boomtick-pkg/mcp/src/tools/github.create_issue.ts b/boomtick-pkg/mcp/src/tools/github.create_issue.ts new file mode 100644 index 0000000000..2b694f7f22 --- /dev/null +++ b/boomtick-pkg/mcp/src/tools/github.create_issue.ts @@ -0,0 +1,43 @@ +import { z } from "zod"; +import { runCommand } from "../lib/shell.js"; +import { sanitizeError } from "../lib/error_utils.js"; + +export const CreateIssueInputSchema = z.object({ + title: z.string().min(1, "Issue title cannot be empty").describe("The title of the issue."), + body: z.string().min(1, "Issue body cannot be empty").describe("The body/description of the issue."), +}); + +const CreateIssueOutputSchema = z.object({ + status: z.string(), + issue: z.object({ + number: z.number(), + title: z.string(), + html_url: z.string(), + state: z.string(), + }).optional(), + message: z.string().optional(), +}); + +export async function createIssueHandler(args: z.infer) { + const params = CreateIssueInputSchema.parse(args); + + const result = await runCommand("td-cli", ["gh", "create-issue", "--title", params.title, "--body", params.body]); + + if (result.exitCode !== 0) { + throw new Error(`Failed to create issue: ${sanitizeError(result.stderr)}`); + } + + let output; + try { + output = JSON.parse(result.stdout); + } catch (e) { + throw new Error(`Failed to parse CLI output: ${result.stdout}`); + } + + const parsedOutput = CreateIssueOutputSchema.parse(output); + if (parsedOutput.status === "error") { + throw new Error(`Failed to create issue: ${parsedOutput.message}`); + } + + return { status: "success", issue: parsedOutput.issue }; +} From 3a95f328e1ca47d4b95ae30504614ac2aeb08351 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 28 Jun 2026 04:51:37 +0000 Subject: [PATCH 2/5] feat(mcp): implement github.create_issue and fix CI environment issues - Implement github.create_issue MCP tool wrapping td-cli - Fix sys.path in td_cli.py to resolve tdw_services package - Update package.json audit:anti-patterns to include required PYTHONPATH - Add unit tests for github.create_issue handler --- boomtick-pkg/cli/dev_tools/td_cli.py | 9 +++++++-- package.json | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/boomtick-pkg/cli/dev_tools/td_cli.py b/boomtick-pkg/cli/dev_tools/td_cli.py index 4135175c31..2a1cf98421 100755 --- a/boomtick-pkg/cli/dev_tools/td_cli.py +++ b/boomtick-pkg/cli/dev_tools/td_cli.py @@ -15,8 +15,13 @@ if "pytest" not in sys.modules: sys.exit(1) -# Add the dev-tools directory to sys.path so we can import tdw_services -sys.path.append(os.path.dirname(os.path.abspath(__file__))) +# Add the CLI package root and dev-tools directory to sys.path so we can import tdw_services and its dependencies +current_dir = os.path.dirname(os.path.abspath(__file__)) +package_root = os.path.dirname(current_dir) +if package_root not in sys.path: + sys.path.append(package_root) +if current_dir not in sys.path: + sys.path.append(current_dir) try: from tdw_services.cli import cli diff --git a/package.json b/package.json index 74773a589c..06f2d895b7 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "lint:eslint": "eslint . --max-warnings 0", "lint:types": "tsc --noEmit", "audit:semgrep": "semgrep scan --config auto --error", - "audit:anti-patterns": "node scripts/detect-antipatterns.mjs", + "audit:anti-patterns": "PYTHONPATH=boomtick-pkg/cli:boomtick-pkg/cli/dev_tools node scripts/detect-antipatterns.mjs", "audit:dead-code": "pnpm exec knip --exclude exports", "ci:local": "run-s lint type-check test audit:anti-patterns audit:dead-code audit:semgrep", "knip": "knip --exclude exports", From 20687c4131c63274d3581006ab5b1e18c23fe615 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 28 Jun 2026 04:58:47 +0000 Subject: [PATCH 3/5] feat(mcp): implement github.create_issue tool and fix CI environment issues - Implement github.create_issue MCP tool wrapping td-cli - Fix sys.path in td_cli.py to resolve tdw_services package without external PYTHONPATH - Update package.json audit:anti-patterns script to include required PYTHONPATH - Add comprehensive unit tests for github.create_issue handler in MCP server From 5b4dca2aef1e136c306cd6ff46ebb017c09d7d57 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 28 Jun 2026 09:56:58 +0000 Subject: [PATCH 4/5] feat(mcp): implement github.create_issue and fix CI environment issues - Implement github.create_issue MCP tool wrapping td-cli - Fix sys.path in td_cli.py to resolve tdw_services package without external PYTHONPATH - Update package.json audit:anti-patterns script to include required PYTHONPATH - Add comprehensive unit tests for github.create_issue handler in MCP server From bb8b6bf1302c4a76a10c6f69d3e64d14f433f990 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 09:44:00 +0000 Subject: [PATCH 5/5] feat(mcp): implement github.create_issue and fix CI environment issues - Implement github.create_issue MCP tool wrapping td-cli - Fix sys.path in td_cli.py to resolve tdw_services package without external PYTHONPATH - Update package.json audit:anti-patterns script to include required PYTHONPATH - Add comprehensive unit tests for github.create_issue handler in MCP server