Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
270 changes: 270 additions & 0 deletions docs/src/content/docs/reference/safe-outputs-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,15 @@ safe-outputs:
3. **No Side Effects**: Creates no GitHub resources.
4. **Transparency**: Provides clear indication of normal completion vs. error states.

**Configuration Parameters**:
- `max`: Operation limit (always 1; noop is registered as a singleton type)
- `message`: Default completion message (overridden by agent-provided message at invocation time)

**Security Requirements**:
- The `message` field MUST undergo content sanitization to prevent log injection
- The handler MUST NOT make any GitHub API calls
- The handler MUST NOT modify any workflow state or create side effects

**Required Permissions**:

*GitHub Actions Token*:
Expand Down Expand Up @@ -2057,6 +2066,53 @@ This section provides complete definitions for all remaining safe output types.
**Cross-Repository Support**: Yes
**Mandatory**: No

**MCP Tool Schema**:

```json
{
"name": "update_issue",
"description": "Update an existing GitHub issue's status, title, body, labels, assignees, or milestone. Only the fields you specify will be updated; other fields remain unchanged.",
"inputSchema": {
"type": "object",
"properties": {
"status": {"type": "string", "enum": ["open", "closed"], "description": "New issue status"},
"title": {"type": "string", "description": "New issue title"},
"body": {"type": "string", "description": "New issue body in Markdown"},
"issue_number": {"type": ["number", "string"], "description": "Issue number to update"},
"operation": {
"type": "string",
"enum": ["replace", "append", "prepend", "replace-island"],
"description": "Body update mode (default: append)"
},
"labels": {"type": "array", "items": {"type": "string"}, "description": "Replacement label list"},
"assignees": {"type": "array", "items": {"type": "string"}, "description": "Replacement assignee list"},
"milestone": {"type": ["number", "string"], "description": "Milestone number (null to clear)"}
},
Comment on lines +2078 to +2090
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property descriptions in the update_issue schema are overly simplified compared to the actual tool schema. For example, the issue_number description should clarify that it's required when the workflow target is '*' (any issue) and should include an example format (e.g., "789 in github.com/owner/repo/issues/789"). The operation description should also explain what each mode does rather than just saying "Body update mode". Consider matching the detailed descriptions from safe_outputs_tools.json lines 621-675.

This issue also appears on line 2074 of the same file.

Copilot uses AI. Check for mistakes.
"additionalProperties": false
}
}
```

**Operational Semantics**:

1. **Partial Updates**: Only fields explicitly provided are modified; omitted fields are unchanged.
2. **Body Operation Modes**: `replace` overwrites the entire body; `append`/`prepend` add content with separator; `replace-island` updates a run-specific section.
3. **Label Validation**: Provided labels must exist in the repository; non-existent labels cause failure.
4. **Assignee Resolution**: Assignees must have repository access; invalid usernames cause failure.
5. **Cross-Repository**: When `target-repo` is configured, operates on that repository (must be in `allowed-repos`).

**Configuration Parameters**:
- `max`: Operation limit (default: 1)
- `target-repo`: Cross-repository target
- `allowed-repos`: Cross-repo allowlist
- `staged`: Staged mode override

**Security Requirements**:
- Title and body MUST undergo full content sanitization before modification
- Label values MUST be validated against repository labels before application
- Cross-repository targets MUST be validated against the `allowed-repos` allowlist
- Issue number MUST be validated as a positive integer belonging to the target repository

**Required Permissions**:

*GitHub Actions Token*:
Expand All @@ -2081,6 +2137,48 @@ This section provides complete definitions for all remaining safe output types.
**Cross-Repository Support**: Yes
**Mandatory**: No

**MCP Tool Schema**:

```json
{
"name": "close_issue",
"description": "Close a GitHub issue with a closing comment explaining the resolution or reason for closing.",
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MCP Tool Schema for close_issue has a simplified description that differs from the actual schema in safe_outputs_tools.json. The actual description (line 147) provides important context about when to use this tool versus update_issue, explains idempotent behavior, and clarifies usage scenarios. Consider using the exact description to maintain consistency and completeness.

This issue also appears on line 2151 of the same file.

Suggested change
"description": "Close a GitHub issue with a closing comment explaining the resolution or reason for closing.",
"description": "Close a GitHub issue by posting a final closing comment that explains the resolution or reason for closing. Use this when the issue's work is complete and it should be closed, rather than `update_issue` which is for modifying still-open issues. This operation is idempotent: if the issue is already closed, it will only add the closing comment.",

Copilot uses AI. Check for mistakes.
"inputSchema": {
"type": "object",
"required": ["body"],
"properties": {
"body": {"type": "string", "description": "Closing comment explaining the resolution"},
"issue_number": {
"type": ["number", "string"],
"description": "Issue number to close. If omitted, closes the issue that triggered this workflow."
}
},
"additionalProperties": false
}
}
```

**Operational Semantics**:

1. **Comment First**: A closing comment is posted before the issue state is changed to `closed`.
2. **Context Resolution**: When `issue_number` is omitted, resolves from the workflow trigger context.
3. **Idempotent Comment**: If the issue is already closed, the closing comment is still posted.
4. **Cross-Repository**: When `target-repo` is configured, operates on that repository (must be in `allowed-repos`).
5. **Footer Injection**: Appends attribution footer to the closing comment when configured.

**Configuration Parameters**:
- `max`: Operation limit (default: 1)
- `target-repo`: Cross-repository target
- `allowed-repos`: Cross-repo allowlist
- `footer`: Footer override
- `staged`: Staged mode override

**Security Requirements**:
- The closing comment body MUST undergo full content sanitization
- Issue number MUST be validated as a positive integer belonging to the target repository
- Cross-repository targets MUST be validated against the `allowed-repos` allowlist
- The handler MUST verify the caller has `issues: write` permission before executing

**Required Permissions**:

*GitHub Actions Token*:
Expand All @@ -2101,6 +2199,48 @@ This section provides complete definitions for all remaining safe output types.
**Cross-Repository Support**: No (same repository only)
**Mandatory**: No

**MCP Tool Schema**:

```json
{
"name": "link_sub_issue",
"description": "Link an issue as a sub-issue of a parent issue, establishing a parent-child relationship.",
"inputSchema": {
"type": "object",
"required": ["parent_issue_number", "sub_issue_number"],
"properties": {
"parent_issue_number": {
"type": ["number", "string"],
"description": "The parent issue number to link the sub-issue to"
},
"sub_issue_number": {
"type": ["number", "string"],
"description": "The issue number to link as a sub-issue of the parent"
Comment on lines +2214 to +2218
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property descriptions for parent_issue_number and sub_issue_number are simplified. The actual schema (lines 846-859) includes helpful examples showing the numeric ID format from GitHub URLs. Consider matching those detailed descriptions for better usability.

This issue also appears in the following locations of the same file:

  • line 2207
  • line 2241
  • line 2234
  • line 2235
  • line 2230
Suggested change
"description": "The parent issue number to link the sub-issue to"
},
"sub_issue_number": {
"type": ["number", "string"],
"description": "The issue number to link as a sub-issue of the parent"
"description": "The numeric ID of the parent issue, as it appears in the GitHub URL (for example, for https://github.com/OWNER/REPO/issues/123 use 123)."
},
"sub_issue_number": {
"type": ["number", "string"],
"description": "The numeric ID of the issue to link as a sub-issue, as it appears in the GitHub URL (for example, for https://github.com/OWNER/REPO/issues/456 use 456)."

Copilot uses AI. Check for mistakes.
}
},
"additionalProperties": false
}
}
```

**Operational Semantics**:

1. **Task List Insertion**: Adds a task list entry referencing the sub-issue to the parent issue body.
2. **Bidirectional Navigation**: Creates navigable links from parent to child and child to parent.
3. **Limit Enforcement**: Enforces maximum sub-issue count (default: 50) per parent issue.
4. **Validation**: Both parent and sub-issue numbers must exist in the same repository.
5. **Same Repository Only**: Cross-repository sub-issue linking is not supported.

**Configuration Parameters**:
- `max`: Operation limit (default: 1)
- `staged`: Staged mode override

**Security Requirements**:
- Both `parent_issue_number` and `sub_issue_number` MUST be validated as positive integers
- Both issue numbers MUST exist in the target repository before modification
- The handler MUST enforce the maximum sub-issue count limit to prevent unbounded growth
- Cross-repository operations MUST be rejected; only same-repository linking is permitted

**Required Permissions**:

*GitHub Actions Token*:
Expand All @@ -2125,6 +2265,49 @@ This section provides complete definitions for all remaining safe output types.
**Cross-Repository Support**: Yes
**Mandatory**: No

**MCP Tool Schema**:

```json
{
"name": "create_discussion",
"description": "Create a GitHub discussion for announcements, Q&A, reports, status updates, or community conversations.",
"inputSchema": {
"type": "object",
"required": ["title", "body"],
"properties": {
"title": {"type": "string", "description": "Discussion title"},
Comment on lines +2273 to +2278
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MCP Tool Schema for create_discussion has a simplified description. The actual schema (line 62) provides important guidance about when to use this tool versus create_issue, explaining use cases and benefits. Consider using the exact description from safe_outputs_tools.json for consistency and completeness.

This issue also appears on line 2278 of the same file.

Suggested change
"description": "Create a GitHub discussion for announcements, Q&A, reports, status updates, or community conversations.",
"inputSchema": {
"type": "object",
"required": ["title", "body"],
"properties": {
"title": {"type": "string", "description": "Discussion title"},
"description": "Create a GitHub discussion instead of an issue when you want an open-ended, collaborative conversation (for example, announcements, Q&A, RFCs, status updates, or community feedback) that is not directly tracked as actionable work. Use `create_issue` for concrete tasks, bugs, and work items that need to be tracked and closed.",
"inputSchema": {
"type": "object",
"required": ["title", "body"],
"properties": {
"title": {"type": "string", "description": "Clear, human-readable discussion title that summarizes the topic or question (not an issue or bug summary)."},

Copilot uses AI. Check for mistakes.
"body": {"type": "string", "description": "Discussion body in Markdown"},
"category": {
"type": "string",
"description": "Discussion category by name, slug, or ID. Defaults to first available category."
}
},
"additionalProperties": false
}
}
```

**Operational Semantics**:

1. **Category Resolution**: Category is resolved by name, slug, or ID; defaults to the first available category if omitted.
2. **Fallback Behavior**: When repository discussions are disabled, falls back to creating an issue if `issues: write` is available.
3. **Footer Injection**: Appends attribution footer to the discussion body when configured.
4. **Cross-Repository**: When `target-repo` is configured, creates in that repository (must be in `allowed-repos`).
5. **Temporary ID Support**: Supports `temporary_id` field for referencing before creation.

**Configuration Parameters**:
- `max`: Operation limit (default: 1)
- `category`: Default discussion category
- `target-repo`: Cross-repository target
- `allowed-repos`: Cross-repo allowlist
- `footer`: Footer override
- `staged`: Staged mode override

**Security Requirements**:
- Title and body MUST undergo full content sanitization before creation
- Category MUST be validated as an existing category in the target repository
- Cross-repository targets MUST be validated against the `allowed-repos` allowlist

**Required Permissions**:

*GitHub Actions Token*:
Expand All @@ -2151,6 +2334,46 @@ This section provides complete definitions for all remaining safe output types.
**Cross-Repository Support**: Yes
**Mandatory**: No

**MCP Tool Schema**:

```json
{
"name": "update_discussion",
"description": "Update an existing GitHub discussion's title or body. Only the fields you specify will be updated.",
"inputSchema": {
"type": "object",
"properties": {
"title": {"type": "string", "description": "New discussion title"},
"body": {"type": "string", "description": "New discussion body in Markdown"},
Comment on lines +2342 to +2347
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MCP Tool Schema for update_discussion has a simplified description. The actual schema (line 88) provides important context about partial updates. Consider using the exact description from safe_outputs_tools.json for consistency.

This issue also appears on line 2346 of the same file.

Suggested change
"description": "Update an existing GitHub discussion's title or body. Only the fields you specify will be updated.",
"inputSchema": {
"type": "object",
"properties": {
"title": {"type": "string", "description": "New discussion title"},
"body": {"type": "string", "description": "New discussion body in Markdown"},
"description": "Partially update an existing GitHub discussion's title or body. Only fields explicitly provided in the input will be modified; any omitted fields will retain their existing values.",
"inputSchema": {
"type": "object",
"properties": {
"title": {"type": "string", "description": "Optional. New discussion title. If omitted, the existing title will be preserved."},
"body": {"type": "string", "description": "Optional. New discussion body in Markdown. If omitted, the existing body will be preserved."},

Copilot uses AI. Check for mistakes.
"discussion_number": {
"type": ["number", "string"],
"description": "Discussion number to update. Required when workflow target is '*'."
}
},
"additionalProperties": false
}
}
```

**Operational Semantics**:

1. **Partial Updates**: Only fields explicitly provided are modified; omitted fields are unchanged.
2. **Context Resolution**: When `discussion_number` is omitted, resolves from the workflow trigger context.
3. **Cross-Repository**: When `target-repo` is configured, operates on that repository (must be in `allowed-repos`).
4. **GraphQL-Based**: Uses GitHub GraphQL API for discussion updates as the REST API does not support discussion modification.

**Configuration Parameters**:
- `max`: Operation limit (default: 1)
- `target-repo`: Cross-repository target
- `allowed-repos`: Cross-repo allowlist
- `staged`: Staged mode override

**Security Requirements**:
- Title and body MUST undergo full content sanitization before modification
- Discussion number MUST be validated as a positive integer belonging to the target repository
- Cross-repository targets MUST be validated against the `allowed-repos` allowlist
- At least one of `title` or `body` MUST be provided; empty updates MUST be rejected

**Required Permissions**:

*GitHub Actions Token*:
Expand All @@ -2171,6 +2394,53 @@ This section provides complete definitions for all remaining safe output types.
**Cross-Repository Support**: Yes
**Mandatory**: No

**MCP Tool Schema**:

```json
{
"name": "close_discussion",
"description": "Close a GitHub discussion with a resolution comment and optional reason.",
"inputSchema": {
"type": "object",
"required": ["body"],
"properties": {
"body": {"type": "string", "description": "Closing comment explaining why the discussion is being closed"},
Comment on lines +2402 to +2407
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MCP Tool Schema for close_discussion has a simplified description. The actual schema (line 113) provides important guidance about when to use this tool and explains idempotent behavior. Consider using the exact description from safe_outputs_tools.json for consistency and completeness.

This issue also appears on line 2407 of the same file.

Suggested change
"description": "Close a GitHub discussion with a resolution comment and optional reason.",
"inputSchema": {
"type": "object",
"required": ["body"],
"properties": {
"body": {"type": "string", "description": "Closing comment explaining why the discussion is being closed"},
"description": "Close a GitHub discussion by posting a final resolution comment and optional reason. Use this tool only when the discussion outcome is clear and no further assistant actions are needed. This operation is idempotent: if the discussion is already closed in the same state, calling this tool again has no additional effect.",
"inputSchema": {
"type": "object",
"required": ["body"],
"properties": {
"body": {"type": "string", "description": "Final closing comment that clearly explains the resolution, summarizes the outcome, and states why the discussion is being closed"},

Copilot uses AI. Check for mistakes.
"reason": {
"type": "string",
"enum": ["RESOLVED", "DUPLICATE", "OUTDATED", "ANSWERED"],
"description": "Resolution reason"
},
"discussion_number": {
"type": ["number", "string"],
"description": "Discussion number to close. If omitted, closes the discussion that triggered this workflow."
}
},
"additionalProperties": false
}
}
```

**Operational Semantics**:

1. **Comment First**: A closing comment is posted before the discussion state is changed to `closed`.
2. **Resolution Reason**: Optional reason (`RESOLVED`, `DUPLICATE`, `OUTDATED`, `ANSWERED`) is recorded via GraphQL API.
3. **Context Resolution**: When `discussion_number` is omitted, resolves from the workflow trigger context.
4. **Idempotent Comment**: If the discussion is already closed, the closing comment is still posted.
5. **Cross-Repository**: When `target-repo` is configured, operates on that repository (must be in `allowed-repos`).

**Configuration Parameters**:
- `max`: Operation limit (default: 1)
- `target-repo`: Cross-repository target
- `allowed-repos`: Cross-repo allowlist
- `footer`: Footer override
- `staged`: Staged mode override

**Security Requirements**:
- The closing comment body MUST undergo full content sanitization
- Discussion number MUST be validated as a positive integer belonging to the target repository
- Cross-repository targets MUST be validated against the `allowed-repos` allowlist
- The `reason` field MUST be validated against the allowed enum values before submission

**Required Permissions**:

*GitHub Actions Token*:
Expand Down