create_pull_request
Automate GitHub pull request creation with proper authentication. Specify title, description, base and head branches, and working directory. Supports dry runs for previewing changes.
Instructions
Create a GitHub pull request with proper authentication handling
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| baseBranch | No | Base branch (target) | main |
| body | Yes | Pull request description | |
| dryRun | No | Preview without executing | |
| headBranch | No | Head branch (source, defaults to current branch) | |
| title | Yes | Pull request title | |
| workingDir | No | Working directory path |
Input Schema (JSON Schema)
{
"properties": {
"baseBranch": {
"default": "main",
"description": "Base branch (target)",
"type": "string"
},
"body": {
"description": "Pull request description",
"type": "string"
},
"dryRun": {
"default": false,
"description": "Preview without executing",
"type": "boolean"
},
"headBranch": {
"description": "Head branch (source, defaults to current branch)",
"type": "string"
},
"title": {
"description": "Pull request title",
"type": "string"
},
"workingDir": {
"description": "Working directory path",
"type": "string"
}
},
"required": [
"title",
"body"
],
"type": "object"
}
Implementation Reference
- src/index.ts:236-292 (handler)The main handler function that implements the create_pull_request tool. It uses GitHub CLI (gh pr create) to create a pull request, with proper token clearing and input escaping to handle authentication securely.async function createPullRequest( title: string, body: string, baseBranch: string = 'main', headBranch?: string, workingDir?: string, dryRun: boolean = false ): Promise<WorkflowResult> { try { if (dryRun) { return { success: true, message: "Dry run: Would create pull request", details: { title, body, baseBranch, headBranch } }; } // Get current branch if not specified if (!headBranch) { const git = getGit(workingDir); headBranch = await git.revparse(['--abbrev-ref', 'HEAD']); } // Create PR using GitHub CLI with cleared tokens and proper escaping const command = `gh pr create --title "${title}" --body "${body}" --base ${baseBranch} --head ${headBranch}`; const result = await executeGitHubCommand(command, { title, body, cwd: workingDir }); // Extract PR URL from output const prUrl = result.stdout.trim(); return { success: true, message: "Successfully created pull request", details: { url: prUrl, title, baseBranch, headBranch } }; } catch (error: any) { return { success: false, message: "Failed to create pull request", error: `Failed to create pull request: ${error.message}` }; } }
- src/index.ts:454-485 (schema)The input schema definition for the create_pull_request tool, specifying parameters like title, body, branches, and options.inputSchema: { type: "object", properties: { title: { type: "string", description: "Pull request title" }, body: { type: "string", description: "Pull request description" }, baseBranch: { type: "string", description: "Base branch (target)", default: "main" }, headBranch: { type: "string", description: "Head branch (source, defaults to current branch)" }, workingDir: { type: "string", description: "Working directory path" }, dryRun: { type: "boolean", description: "Preview without executing", default: false } }, required: ["title", "body"] }
- src/index.ts:595-604 (registration)The dispatch/registration in the CallToolRequestSchema handler that routes calls to the createPullRequest function.case "create_pull_request": result = await createPullRequest( args?.title as string, args?.body as string, (args?.baseBranch as string) || 'main', args?.headBranch as string, args?.workingDir as string, (args?.dryRun as boolean) || false ); break;
- src/index.ts:452-486 (registration)The tool registration in the ListToolsRequestSchema response, including name, description, and schema.name: "create_pull_request", description: "Create a GitHub pull request with proper authentication handling", inputSchema: { type: "object", properties: { title: { type: "string", description: "Pull request title" }, body: { type: "string", description: "Pull request description" }, baseBranch: { type: "string", description: "Base branch (target)", default: "main" }, headBranch: { type: "string", description: "Head branch (source, defaults to current branch)" }, workingDir: { type: "string", description: "Working directory path" }, dryRun: { type: "boolean", description: "Preview without executing", default: false } }, required: ["title", "body"] } },
- src/index.ts:65-115 (helper)Helper function used by createPullRequest to execute GitHub CLI commands securely, clearing tokens and handling PowerShell escaping.async function executeGitHubCommand( baseCommand: string, options?: { title?: string; body?: string; cwd?: string; } ): Promise<{ stdout: string; stderr: string }> { let tempBodyFile: string | null = null; try { let finalCommand = baseCommand; // If we have a body, write it to a temporary file to avoid PowerShell parsing issues if (options?.body) { tempBodyFile = join(tmpdir(), `gh-pr-body-${Date.now()}.txt`); writeFileSync(tempBodyFile, options.body, 'utf8'); // Replace --body with --body-file, handling both quoted and unquoted body content finalCommand = finalCommand.replace(/--body\s+"[^"]*"/, `--body-file "${tempBodyFile}"`); finalCommand = finalCommand.replace(/--body\s+[^\s]+/, `--body-file "${tempBodyFile}"`); } // If we have a title, properly escape it for PowerShell if (options?.title) { const escapedTitle = escapePowerShellString(options.title); finalCommand = finalCommand.replace(/--title\s+"([^"]*)"/, `--title '${escapedTitle}'`); } // Build the PowerShell command with proper token clearing const powerShellCommand = `powershell -Command "Remove-Item Env:GH_TOKEN -ErrorAction SilentlyContinue; Remove-Item Env:GITHUB_TOKEN -ErrorAction SilentlyContinue; ${finalCommand}"`; const result = await execAsync(powerShellCommand, { cwd: options?.cwd || process.cwd(), maxBuffer: 1024 * 1024 // 1MB buffer }); return result; } catch (error: any) { throw new Error(`Command failed: ${error.message}\nStdout: ${error.stdout}\nStderr: ${error.stderr}`); } finally { // Clean up temporary file if (tempBodyFile) { try { unlinkSync(tempBodyFile); } catch (e) { // Ignore cleanup errors } } } }