diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 233b8f39bf..31bd8d077f 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -353,38 +353,38 @@ jobs: "name": "noop" }, { - "description": "Post a message to a Slack channel. Message must be 200 characters or less. Supports basic Slack markdown: *bold*, _italic_, ~strike~, `code`, ```code block```, \u003equote, and links \u003curl|text\u003e. Requires GH_AW_SLACK_CHANNEL_ID environment variable to be set.", + "description": "Add a comment to a Notion page", "inputSchema": { "additionalProperties": false, "properties": { - "message": { - "description": "The message to post (max 200 characters, supports Slack markdown)", + "comment": { + "description": "The comment text to add", "type": "string" } }, "required": [ - "message" + "comment" ], "type": "object" }, - "name": "post_to_slack_channel" + "name": "notion_add_comment" }, { - "description": "Add a comment to a Notion page", + "description": "Post a message to a Slack channel. Message must be 200 characters or less. Supports basic Slack markdown: *bold*, _italic_, ~strike~, `code`, ```code block```, \u003equote, and links \u003curl|text\u003e. Requires GH_AW_SLACK_CHANNEL_ID environment variable to be set.", "inputSchema": { "additionalProperties": false, "properties": { - "comment": { - "description": "The comment text to add", + "message": { + "description": "The message to post (max 200 characters, supports Slack markdown)", "type": "string" } }, "required": [ - "comment" + "message" ], "type": "object" }, - "name": "notion_add_comment" + "name": "post_to_slack_channel" } ] EOF diff --git a/actions/setup/js/safe_outputs_tools.json b/actions/setup/js/safe_outputs_tools.json index a14e27adfd..fd71f0e703 100644 --- a/actions/setup/js/safe_outputs_tools.json +++ b/actions/setup/js/safe_outputs_tools.json @@ -4,7 +4,10 @@ "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead.", "inputSchema": { "type": "object", - "required": ["title", "body"], + "required": [ + "body", + "title" + ], "properties": { "title": { "type": "string", @@ -22,7 +25,10 @@ "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository." }, "parent": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123def456') from a previously created issue in the same workflow run." }, "temporary_id": { @@ -38,7 +44,9 @@ "description": "Create a GitHub Copilot agent task to delegate coding work. Use this when you need another Copilot agent to implement code changes, fix bugs, or complete development tasks. The task becomes a new issue that triggers the Copilot coding agent. For non-coding tasks or manual work items, use create_issue instead.", "inputSchema": { "type": "object", - "required": ["body"], + "required": [ + "body" + ], "properties": { "body": { "type": "string", @@ -53,7 +61,10 @@ "description": "Create a GitHub discussion for announcements, Q&A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead.", "inputSchema": { "type": "object", - "required": ["title", "body"], + "required": [ + "body", + "title" + ], "properties": { "title": { "type": "string", @@ -86,7 +97,10 @@ "description": "New discussion body to replace the existing content. Use Markdown formatting." }, "discussion_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Discussion number to update. This is the numeric ID from the GitHub URL (e.g., 345 in github.com/owner/repo/discussions/345). Required when the workflow target is '*' (any discussion)." } }, @@ -98,7 +112,9 @@ "description": "Close a GitHub discussion with a resolution comment and optional reason. Use this to mark discussions as resolved, answered, or no longer needed. The closing comment should explain why the discussion is being closed.", "inputSchema": { "type": "object", - "required": ["body"], + "required": [ + "body" + ], "properties": { "body": { "type": "string", @@ -106,11 +122,19 @@ }, "reason": { "type": "string", - "enum": ["RESOLVED", "DUPLICATE", "OUTDATED", "ANSWERED"], + "enum": [ + "RESOLVED", + "DUPLICATE", + "OUTDATED", + "ANSWERED" + ], "description": "Resolution reason: RESOLVED (issue addressed), DUPLICATE (discussed elsewhere), OUTDATED (no longer relevant), or ANSWERED (question answered)." }, "discussion_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Discussion number to close. This is the numeric ID from the GitHub URL (e.g., 678 in github.com/owner/repo/discussions/678). If omitted, closes the discussion that triggered this workflow (requires a discussion event trigger)." } }, @@ -122,14 +146,19 @@ "description": "Close a GitHub issue with a closing comment. Use this when work is complete, the issue is no longer relevant, or it's a duplicate. The closing comment should explain the resolution or reason for closing.", "inputSchema": { "type": "object", - "required": ["body"], + "required": [ + "body" + ], "properties": { "body": { "type": "string", "description": "Closing comment explaining why the issue is being closed and summarizing any resolution, workaround, or conclusion." }, "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to close. This is the numeric ID from the GitHub URL (e.g., 901 in github.com/owner/repo/issues/901). If omitted, closes the issue that triggered this workflow (requires an issue event trigger)." } }, @@ -141,14 +170,19 @@ "description": "Close a pull request WITHOUT merging, adding a closing comment. Use this for PRs that should be abandoned, superseded, or closed for other reasons. The closing comment should explain why the PR is being closed. This does NOT merge the changes.", "inputSchema": { "type": "object", - "required": ["body"], + "required": [ + "body" + ], "properties": { "body": { "type": "string", "description": "Closing comment explaining why the PR is being closed without merging (e.g., superseded by another PR, no longer needed, approach rejected)." }, "pull_request_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Pull request number to close. This is the numeric ID from the GitHub URL (e.g., 432 in github.com/owner/repo/pull/432). If omitted, closes the PR that triggered this workflow (requires a pull_request event trigger)." } }, @@ -160,7 +194,10 @@ "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead.", "inputSchema": { "type": "object", - "required": ["body", "item_number"], + "required": [ + "body", + "item_number" + ], "properties": { "body": { "type": "string", @@ -179,7 +216,10 @@ "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead.", "inputSchema": { "type": "object", - "required": ["title", "body"], + "required": [ + "body", + "title" + ], "properties": { "title": { "type": "string", @@ -209,14 +249,21 @@ "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead.", "inputSchema": { "type": "object", - "required": ["path", "line", "body"], + "required": [ + "body", + "line", + "path" + ], "properties": { "path": { "type": "string", "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR." }, "line": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line." }, "body": { @@ -224,12 +271,18 @@ "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location." }, "start_line": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments." }, "side": { "type": "string", - "enum": ["LEFT", "RIGHT"], + "enum": [ + "LEFT", + "RIGHT" + ], "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT." } }, @@ -241,19 +294,32 @@ "description": "Create a code scanning alert for security vulnerabilities, code quality issues, or other findings. Alerts appear in the repository's Security tab and integrate with GitHub's security features. Use this for automated security analysis results.", "inputSchema": { "type": "object", - "required": ["file", "line", "severity", "message"], + "required": [ + "file", + "line", + "message", + "severity" + ], "properties": { "file": { "type": "string", "description": "File path relative to the repository root where the issue was found (e.g., 'src/auth/password.js')." }, "line": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Line number where the issue was found in the file." }, "severity": { "type": "string", - "enum": ["error", "warning", "info", "note"], + "enum": [ + "error", + "warning", + "info", + "note" + ], "description": "Alert severity level: 'error' (critical security issues), 'warning' (potential problems), 'info' (informational), or 'note' (minor observations)." }, "message": { @@ -261,7 +327,10 @@ "description": "Clear description of the security issue or finding. Include what's wrong and ideally how to fix it." }, "column": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Column number for more precise location of the issue within the line." }, "ruleIdSuffix": { @@ -277,7 +346,9 @@ "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead.", "inputSchema": { "type": "object", - "required": ["labels"], + "required": [ + "labels" + ], "properties": { "labels": { "type": "array", @@ -299,7 +370,9 @@ "description": "Add reviewers to a GitHub pull request. Reviewers receive notifications and can approve or request changes. Use 'copilot' as a reviewer name to request the Copilot PR review bot.", "inputSchema": { "type": "object", - "required": ["reviewers"], + "required": [ + "reviewers" + ], "properties": { "reviewers": { "type": "array", @@ -309,7 +382,10 @@ "description": "GitHub usernames to add as reviewers (e.g., ['octocat', 'copilot']). Users must have access to the repository." }, "pull_request_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Pull request number to add reviewers to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds reviewers to the PR that triggered this workflow." } }, @@ -321,14 +397,23 @@ "description": "Assign an issue to a milestone for release planning and progress tracking. Milestones must exist in the repository before assignment.", "inputSchema": { "type": "object", - "required": ["issue_number", "milestone_number"], + "required": [ + "issue_number", + "milestone_number" + ], "properties": { "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to assign to the milestone. This is the numeric ID from the GitHub URL (e.g., 567 in github.com/owner/repo/issues/567)." }, "milestone_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Milestone number (not title) to assign the issue to. This is the numeric ID from the milestone URL (e.g., 12 in github.com/owner/repo/milestone/12). Find milestone numbers in the repository's Milestones page." } }, @@ -340,10 +425,15 @@ "description": "Assign the GitHub Copilot coding agent to work on an issue. The agent will analyze the issue and attempt to implement a solution, creating a pull request when complete. Use this to delegate coding tasks to Copilot.", "inputSchema": { "type": "object", - "required": ["issue_number"], + "required": [ + "issue_number" + ], "properties": { "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to assign the Copilot agent to. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/issues/234). The issue should contain clear, actionable requirements." }, "agent": { @@ -359,10 +449,15 @@ "description": "Assign one or more GitHub users to an issue. Use this to delegate work to specific team members. Users must have access to the repository.", "inputSchema": { "type": "object", - "required": ["issue_number"], + "required": [ + "issue_number" + ], "properties": { "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to assign users to. This is the numeric ID from the GitHub URL (e.g., 543 in github.com/owner/repo/issues/543). If omitted, assigns to the issue that triggered this workflow." }, "assignees": { @@ -388,7 +483,10 @@ "properties": { "status": { "type": "string", - "enum": ["open", "closed"], + "enum": [ + "open", + "closed" + ], "description": "New issue status: 'open' to reopen a closed issue, 'closed' to close an open issue." }, "title": { @@ -400,7 +498,10 @@ "description": "New issue body to replace the existing content. Use Markdown formatting." }, "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to update. This is the numeric ID from the GitHub URL (e.g., 789 in github.com/owner/repo/issues/789). Required when the workflow target is '*' (any issue)." } }, @@ -423,11 +524,18 @@ }, "operation": { "type": "string", - "enum": ["replace", "append", "prepend"], + "enum": [ + "replace", + "append", + "prepend" + ], "description": "How to update the PR body: 'replace' (default - completely overwrite), 'append' (add to end with separator), or 'prepend' (add to start with separator). Title is always replaced." }, "pull_request_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Pull request number to update. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/pull/234). Required when the workflow target is '*' (any PR)." } }, @@ -439,7 +547,9 @@ "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.", "inputSchema": { "type": "object", - "required": ["message"], + "required": [ + "message" + ], "properties": { "branch": { "type": "string", @@ -450,7 +560,10 @@ "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits)." }, "pull_request_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR)." } }, @@ -462,7 +575,9 @@ "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content.", "inputSchema": { "type": "object", - "required": ["path"], + "required": [ + "path" + ], "properties": { "path": { "type": "string", @@ -477,7 +592,11 @@ "description": "Update a GitHub release description by replacing, appending to, or prepending to the existing content. Use this to add release notes, changelogs, or additional information to an existing release.", "inputSchema": { "type": "object", - "required": ["tag", "operation", "body"], + "required": [ + "body", + "operation", + "tag" + ], "properties": { "tag": { "type": "string", @@ -485,7 +604,11 @@ }, "operation": { "type": "string", - "enum": ["replace", "append", "prepend"], + "enum": [ + "replace", + "append", + "prepend" + ], "description": "How to update the release body: 'replace' (completely overwrite), 'append' (add to end with separator), or 'prepend' (add to start with separator)." }, "body": { @@ -501,7 +624,10 @@ "description": "Report that a tool or capability needed to complete the task is not available. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", "inputSchema": { "type": "object", - "required": ["tool", "reason"], + "required": [ + "reason", + "tool" + ], "properties": { "tool": { "type": "string", @@ -524,7 +650,9 @@ "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", "inputSchema": { "type": "object", - "required": ["message"], + "required": [ + "message" + ], "properties": { "message": { "type": "string", @@ -539,14 +667,23 @@ "description": "Link an issue as a sub-issue of a parent issue. Use this to establish parent-child relationships between issues for better organization and tracking of related work items.", "inputSchema": { "type": "object", - "required": ["parent_issue_number", "sub_issue_number"], + "required": [ + "parent_issue_number", + "sub_issue_number" + ], "properties": { "parent_issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "The parent issue number to link the sub-issue to. This is the numeric ID from the GitHub URL (e.g., 100 in github.com/owner/repo/issues/100)." }, "sub_issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "The issue number to link as a sub-issue of the parent. This is the numeric ID from the GitHub URL (e.g., 101 in github.com/owner/repo/issues/101)." } }, @@ -558,7 +695,9 @@ "description": "Hide a comment on a GitHub issue, pull request, or discussion. This collapses the comment and marks it as spam, abuse, off-topic, outdated, or resolved. Use this for inappropriate, off-topic, or outdated comments. The comment_id must be a GraphQL node ID (string like 'IC_kwDOABCD123456'), not a numeric REST API comment ID.", "inputSchema": { "type": "object", - "required": ["comment_id"], + "required": [ + "comment_id" + ], "properties": { "comment_id": { "type": "string", @@ -566,7 +705,13 @@ }, "reason": { "type": "string", - "enum": ["SPAM", "ABUSE", "OFF_TOPIC", "OUTDATED", "RESOLVED"], + "enum": [ + "SPAM", + "ABUSE", + "OFF_TOPIC", + "OUTDATED", + "RESOLVED" + ], "description": "Optional reason for hiding the comment. Defaults to SPAM if not provided. Valid values: SPAM (spam content), ABUSE (abusive/harassment content), OFF_TOPIC (not relevant to discussion), OUTDATED (no longer applicable), RESOLVED (issue/question has been resolved)." } }, @@ -586,7 +731,11 @@ }, "content_type": { "type": "string", - "enum": ["issue", "pull_request", "draft_issue"], + "enum": [ + "issue", + "pull_request", + "draft_issue" + ], "description": "Type of item to add to the project. Use 'issue' or 'pull_request' to add existing repo content, or 'draft_issue' to create a draft item inside the project." }, "content_number": { @@ -616,15 +765,30 @@ }, "oneOf": [ { - "required": ["project", "content_type", "content_number"], + "required": [ + "project", + "content_type", + "content_number" + ], "properties": { - "content_type": { "enum": ["issue", "pull_request"] } + "content_type": { + "enum": [ + "issue", + "pull_request" + ] + } } }, { - "required": ["project", "content_type", "draft_title"], + "required": [ + "project", + "content_type", + "draft_title" + ], "properties": { - "content_type": { "const": "draft_issue" } + "content_type": { + "const": "draft_issue" + } } } ], diff --git a/pkg/workflow/js/safe_outputs_tools.json b/pkg/workflow/js/safe_outputs_tools.json index d0ec9ffcd6..d880eeb989 100644 --- a/pkg/workflow/js/safe_outputs_tools.json +++ b/pkg/workflow/js/safe_outputs_tools.json @@ -4,7 +4,10 @@ "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead.", "inputSchema": { "type": "object", - "required": ["title", "body"], + "required": [ + "body", + "title" + ], "properties": { "title": { "type": "string", @@ -22,7 +25,10 @@ "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository." }, "parent": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123def456') from a previously created issue in the same workflow run." }, "temporary_id": { @@ -38,7 +44,9 @@ "description": "Create a GitHub Copilot agent task to delegate coding work. Use this when you need another Copilot agent to implement code changes, fix bugs, or complete development tasks. The task becomes a new issue that triggers the Copilot coding agent. For non-coding tasks or manual work items, use create_issue instead.", "inputSchema": { "type": "object", - "required": ["body"], + "required": [ + "body" + ], "properties": { "body": { "type": "string", @@ -53,7 +61,10 @@ "description": "Create a GitHub discussion for announcements, Q&A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead.", "inputSchema": { "type": "object", - "required": ["title", "body"], + "required": [ + "body", + "title" + ], "properties": { "title": { "type": "string", @@ -86,7 +97,10 @@ "description": "New discussion body to replace the existing content. Use Markdown formatting." }, "discussion_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Discussion number to update. This is the numeric ID from the GitHub URL (e.g., 345 in github.com/owner/repo/discussions/345). Required when the workflow target is '*' (any discussion)." } }, @@ -98,7 +112,9 @@ "description": "Close a GitHub discussion with a resolution comment and optional reason. Use this to mark discussions as resolved, answered, or no longer needed. The closing comment should explain why the discussion is being closed.", "inputSchema": { "type": "object", - "required": ["body"], + "required": [ + "body" + ], "properties": { "body": { "type": "string", @@ -106,11 +122,19 @@ }, "reason": { "type": "string", - "enum": ["RESOLVED", "DUPLICATE", "OUTDATED", "ANSWERED"], + "enum": [ + "RESOLVED", + "DUPLICATE", + "OUTDATED", + "ANSWERED" + ], "description": "Resolution reason: RESOLVED (issue addressed), DUPLICATE (discussed elsewhere), OUTDATED (no longer relevant), or ANSWERED (question answered)." }, "discussion_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Discussion number to close. This is the numeric ID from the GitHub URL (e.g., 678 in github.com/owner/repo/discussions/678). If omitted, closes the discussion that triggered this workflow (requires a discussion event trigger)." } }, @@ -122,14 +146,19 @@ "description": "Close a GitHub issue with a closing comment. Use this when work is complete, the issue is no longer relevant, or it's a duplicate. The closing comment should explain the resolution or reason for closing.", "inputSchema": { "type": "object", - "required": ["body"], + "required": [ + "body" + ], "properties": { "body": { "type": "string", "description": "Closing comment explaining why the issue is being closed and summarizing any resolution, workaround, or conclusion." }, "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to close. This is the numeric ID from the GitHub URL (e.g., 901 in github.com/owner/repo/issues/901). If omitted, closes the issue that triggered this workflow (requires an issue event trigger)." } }, @@ -141,14 +170,19 @@ "description": "Close a pull request WITHOUT merging, adding a closing comment. Use this for PRs that should be abandoned, superseded, or closed for other reasons. The closing comment should explain why the PR is being closed. This does NOT merge the changes.", "inputSchema": { "type": "object", - "required": ["body"], + "required": [ + "body" + ], "properties": { "body": { "type": "string", "description": "Closing comment explaining why the PR is being closed without merging (e.g., superseded by another PR, no longer needed, approach rejected)." }, "pull_request_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Pull request number to close. This is the numeric ID from the GitHub URL (e.g., 432 in github.com/owner/repo/pull/432). If omitted, closes the PR that triggered this workflow (requires a pull_request event trigger)." } }, @@ -160,7 +194,10 @@ "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead.", "inputSchema": { "type": "object", - "required": ["body", "item_number"], + "required": [ + "body", + "item_number" + ], "properties": { "body": { "type": "string", @@ -179,7 +216,10 @@ "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead.", "inputSchema": { "type": "object", - "required": ["title", "body"], + "required": [ + "body", + "title" + ], "properties": { "title": { "type": "string", @@ -209,14 +249,21 @@ "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead.", "inputSchema": { "type": "object", - "required": ["path", "line", "body"], + "required": [ + "body", + "line", + "path" + ], "properties": { "path": { "type": "string", "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR." }, "line": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line." }, "body": { @@ -224,12 +271,18 @@ "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location." }, "start_line": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments." }, "side": { "type": "string", - "enum": ["LEFT", "RIGHT"], + "enum": [ + "LEFT", + "RIGHT" + ], "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT." } }, @@ -241,19 +294,32 @@ "description": "Create a code scanning alert for security vulnerabilities, code quality issues, or other findings. Alerts appear in the repository's Security tab and integrate with GitHub's security features. Use this for automated security analysis results.", "inputSchema": { "type": "object", - "required": ["file", "line", "severity", "message"], + "required": [ + "file", + "line", + "message", + "severity" + ], "properties": { "file": { "type": "string", "description": "File path relative to the repository root where the issue was found (e.g., 'src/auth/password.js')." }, "line": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Line number where the issue was found in the file." }, "severity": { "type": "string", - "enum": ["error", "warning", "info", "note"], + "enum": [ + "error", + "warning", + "info", + "note" + ], "description": "Alert severity level: 'error' (critical security issues), 'warning' (potential problems), 'info' (informational), or 'note' (minor observations)." }, "message": { @@ -261,7 +327,10 @@ "description": "Clear description of the security issue or finding. Include what's wrong and ideally how to fix it." }, "column": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Column number for more precise location of the issue within the line." }, "ruleIdSuffix": { @@ -277,7 +346,9 @@ "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead.", "inputSchema": { "type": "object", - "required": ["labels"], + "required": [ + "labels" + ], "properties": { "labels": { "type": "array", @@ -299,7 +370,9 @@ "description": "Add reviewers to a GitHub pull request. Reviewers receive notifications and can approve or request changes. Use 'copilot' as a reviewer name to request the Copilot PR review bot.", "inputSchema": { "type": "object", - "required": ["reviewers"], + "required": [ + "reviewers" + ], "properties": { "reviewers": { "type": "array", @@ -309,7 +382,10 @@ "description": "GitHub usernames to add as reviewers (e.g., ['octocat', 'copilot']). Users must have access to the repository." }, "pull_request_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Pull request number to add reviewers to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds reviewers to the PR that triggered this workflow." } }, @@ -321,14 +397,23 @@ "description": "Assign an issue to a milestone for release planning and progress tracking. Milestones must exist in the repository before assignment.", "inputSchema": { "type": "object", - "required": ["issue_number", "milestone_number"], + "required": [ + "issue_number", + "milestone_number" + ], "properties": { "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to assign to the milestone. This is the numeric ID from the GitHub URL (e.g., 567 in github.com/owner/repo/issues/567)." }, "milestone_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Milestone number (not title) to assign the issue to. This is the numeric ID from the milestone URL (e.g., 12 in github.com/owner/repo/milestone/12). Find milestone numbers in the repository's Milestones page." } }, @@ -340,10 +425,15 @@ "description": "Assign the GitHub Copilot coding agent to work on an issue. The agent will analyze the issue and attempt to implement a solution, creating a pull request when complete. Use this to delegate coding tasks to Copilot.", "inputSchema": { "type": "object", - "required": ["issue_number"], + "required": [ + "issue_number" + ], "properties": { "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to assign the Copilot agent to. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/issues/234). The issue should contain clear, actionable requirements." }, "agent": { @@ -359,10 +449,15 @@ "description": "Assign one or more GitHub users to an issue. Use this to delegate work to specific team members. Users must have access to the repository.", "inputSchema": { "type": "object", - "required": ["issue_number"], + "required": [ + "issue_number" + ], "properties": { "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to assign users to. This is the numeric ID from the GitHub URL (e.g., 543 in github.com/owner/repo/issues/543). If omitted, assigns to the issue that triggered this workflow." }, "assignees": { @@ -388,7 +483,10 @@ "properties": { "status": { "type": "string", - "enum": ["open", "closed"], + "enum": [ + "open", + "closed" + ], "description": "New issue status: 'open' to reopen a closed issue, 'closed' to close an open issue." }, "title": { @@ -400,7 +498,10 @@ "description": "New issue body to replace the existing content. Use Markdown formatting." }, "issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Issue number to update. This is the numeric ID from the GitHub URL (e.g., 789 in github.com/owner/repo/issues/789). Required when the workflow target is '*' (any issue)." } }, @@ -423,11 +524,18 @@ }, "operation": { "type": "string", - "enum": ["replace", "append", "prepend"], + "enum": [ + "replace", + "append", + "prepend" + ], "description": "How to update the PR body: 'replace' (default - completely overwrite), 'append' (add to end with separator), or 'prepend' (add to start with separator). Title is always replaced." }, "pull_request_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Pull request number to update. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/pull/234). Required when the workflow target is '*' (any PR)." } }, @@ -439,7 +547,9 @@ "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.", "inputSchema": { "type": "object", - "required": ["message"], + "required": [ + "message" + ], "properties": { "branch": { "type": "string", @@ -450,7 +560,10 @@ "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits)." }, "pull_request_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR)." } }, @@ -462,7 +575,9 @@ "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content.", "inputSchema": { "type": "object", - "required": ["path"], + "required": [ + "path" + ], "properties": { "path": { "type": "string", @@ -477,7 +592,11 @@ "description": "Update a GitHub release description by replacing, appending to, or prepending to the existing content. Use this to add release notes, changelogs, or additional information to an existing release.", "inputSchema": { "type": "object", - "required": ["tag", "operation", "body"], + "required": [ + "body", + "operation", + "tag" + ], "properties": { "tag": { "type": "string", @@ -485,7 +604,11 @@ }, "operation": { "type": "string", - "enum": ["replace", "append", "prepend"], + "enum": [ + "replace", + "append", + "prepend" + ], "description": "How to update the release body: 'replace' (completely overwrite), 'append' (add to end with separator), or 'prepend' (add to start with separator)." }, "body": { @@ -501,7 +624,10 @@ "description": "Report that a tool or capability needed to complete the task is not available. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", "inputSchema": { "type": "object", - "required": ["tool", "reason"], + "required": [ + "reason", + "tool" + ], "properties": { "tool": { "type": "string", @@ -524,7 +650,9 @@ "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", "inputSchema": { "type": "object", - "required": ["message"], + "required": [ + "message" + ], "properties": { "message": { "type": "string", @@ -539,14 +667,23 @@ "description": "Link an issue as a sub-issue of a parent issue. Use this to establish parent-child relationships between issues for better organization and tracking of related work items.", "inputSchema": { "type": "object", - "required": ["parent_issue_number", "sub_issue_number"], + "required": [ + "parent_issue_number", + "sub_issue_number" + ], "properties": { "parent_issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "The parent issue number to link the sub-issue to. This is the numeric ID from the GitHub URL (e.g., 100 in github.com/owner/repo/issues/100)." }, "sub_issue_number": { - "type": ["number", "string"], + "type": [ + "number", + "string" + ], "description": "The issue number to link as a sub-issue of the parent. This is the numeric ID from the GitHub URL (e.g., 101 in github.com/owner/repo/issues/101)." } }, @@ -558,7 +695,9 @@ "description": "Hide a comment on a GitHub issue, pull request, or discussion. This collapses the comment and marks it as spam, abuse, off-topic, outdated, or resolved. Use this for inappropriate, off-topic, or outdated comments. The comment_id must be a GraphQL node ID (string like 'IC_kwDOABCD123456'), not a numeric REST API comment ID.", "inputSchema": { "type": "object", - "required": ["comment_id"], + "required": [ + "comment_id" + ], "properties": { "comment_id": { "type": "string", @@ -566,7 +705,13 @@ }, "reason": { "type": "string", - "enum": ["SPAM", "ABUSE", "OFF_TOPIC", "OUTDATED", "RESOLVED"], + "enum": [ + "SPAM", + "ABUSE", + "OFF_TOPIC", + "OUTDATED", + "RESOLVED" + ], "description": "Optional reason for hiding the comment. Defaults to SPAM if not provided. Valid values: SPAM (spam content), ABUSE (abusive/harassment content), OFF_TOPIC (not relevant to discussion), OUTDATED (no longer applicable), RESOLVED (issue/question has been resolved)." } }, @@ -578,7 +723,10 @@ "description": "Add or update items in GitHub Projects v2 boards. Can add issues/PRs to a project and update custom field values. Requires the project URL, content type (issue or pull_request), and content number. Use campaign_id to group related items.", "inputSchema": { "type": "object", - "required": ["project", "content_type"], + "required": [ + "content_type", + "project" + ], "properties": { "project": { "type": "string", @@ -587,7 +735,11 @@ }, "content_type": { "type": "string", - "enum": ["issue", "pull_request", "draft_issue"], + "enum": [ + "issue", + "pull_request", + "draft_issue" + ], "description": "Type of item to add to the project. Use 'issue' or 'pull_request' to add existing repo content, or 'draft_issue' to create a draft item inside the project." }, "content_number": { diff --git a/pkg/workflow/safe_outputs_config_generation.go b/pkg/workflow/safe_outputs_config_generation.go index bf43d2b412..22d07b9f64 100644 --- a/pkg/workflow/safe_outputs_config_generation.go +++ b/pkg/workflow/safe_outputs_config_generation.go @@ -3,6 +3,7 @@ package workflow import ( "encoding/json" "fmt" + "sort" ) // ======================================== @@ -473,6 +474,8 @@ func generateCustomJobToolDefinition(jobName string, jobConfig *SafeJobConfig) m // Add required fields array if any inputs are required if len(requiredFields) > 0 { + // Sort required fields for stable output + sort.Strings(requiredFields) inputSchema["required"] = requiredFields } diff --git a/pkg/workflow/safe_outputs_custom_job_tools_test.go b/pkg/workflow/safe_outputs_custom_job_tools_test.go index fa5a3188e6..6057155fdd 100644 --- a/pkg/workflow/safe_outputs_custom_job_tools_test.go +++ b/pkg/workflow/safe_outputs_custom_job_tools_test.go @@ -207,3 +207,78 @@ func TestCustomJobToolsWithDifferentInputTypes(t *testing.T) { assert.NotContains(t, required, "count", "Count should not be required") assert.NotContains(t, required, "mode", "Mode should not be required") } + +// TestCustomJobToolsRequiredFieldsAreSorted verifies that the required array +// in custom job tool definitions is always sorted alphabetically for stable output +func TestCustomJobToolsRequiredFieldsAreSorted(t *testing.T) { + workflowData := &WorkflowData{ + SafeOutputs: &SafeOutputsConfig{ + Jobs: map[string]*SafeJobConfig{ + "sorted_required_job": { + Description: "Job to test sorted required fields", + Inputs: map[string]*InputDefinition{ + "zebra": { + Description: "Last alphabetically", + Required: true, + Type: "string", + }, + "alpha": { + Description: "First alphabetically", + Required: true, + Type: "string", + }, + "middle": { + Description: "Middle alphabetically", + Required: true, + Type: "string", + }, + "optional": { + Description: "Not required", + Required: false, + Type: "string", + }, + }, + }, + }, + }, + } + + // Generate the tools JSON + toolsJSON, err := generateFilteredToolsJSON(workflowData) + require.NoError(t, err, "Should generate tools JSON") + + // Parse the JSON + var tools []map[string]any + err = json.Unmarshal([]byte(toolsJSON), &tools) + require.NoError(t, err, "Should parse tools JSON") + + // Find the sorted_required_job tool + var jobTool map[string]any + for _, tool := range tools { + if name, ok := tool["name"].(string); ok && name == "sorted_required_job" { + jobTool = tool + break + } + } + + require.NotNil(t, jobTool, "Should find sorted_required_job tool in tools.json") + + // Verify the input schema + inputSchema, ok := jobTool["inputSchema"].(map[string]any) + require.True(t, ok, "Should have inputSchema") + + // Verify required fields are sorted + required, ok := inputSchema["required"].([]any) + require.True(t, ok, "Should have required array") + assert.Len(t, required, 3, "Should have 3 required fields") + + // Convert to string slice for easier comparison + var requiredStrings []string + for _, r := range required { + requiredStrings = append(requiredStrings, r.(string)) + } + + // Verify the required fields are in sorted order + assert.Equal(t, []string{"alpha", "middle", "zebra"}, requiredStrings, + "Required fields should be sorted alphabetically: alpha, middle, zebra") +}