Skip to content
61 changes: 61 additions & 0 deletions docs/feature-flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,43 @@ end users. Insiders-only flags are not user-toggleable.

## Declaring tool availability

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
## Declaring tool availability
## Declaring tool availability
```Fixes #3554

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
## Declaring tool availability
## Declaring tool availability
```Fixes #3554


### Issue artifacts

Enable `--features=issue_artifacts` (or `GITHUB_FEATURES=issue_artifacts`) to
expose `issue_artifact_read` (`method: list | get`) and `issue_artifact_write`
(`method: create | update | delete`) in the `issues` toolset.
This flag is **off by default**, including in Insiders mode. Read-only mode
exposes only `issue_artifact_read`.

The MCP flag only exposes tools; it does not grant API access. The tools
use the configured authenticated GitHub client and base URL without an
endpoint-specific preview header. They declare `repo` in their OAuth challenge
metadata; API access also depends on the token's permissions and repository
access. API failures are returned explicitly; writes are not retried.

Artifact numbers are scoped to their owning issue, not global IDs. Updates
preserve omitted fields, allow `body: ""` to clear the body, and cannot change
type. Lists return one explicit `page`/`perPage` with bodies; request successive
pages until fewer than `perPage` artifacts are returned. This works with or
without REST `Link` headers; the tools do not expose those headers.
Artifact names and bodies are sanitized as user-authored content.
Both tools fail closed in lockdown mode because the reference artifact
response does not identify authors for content-trust checks.

All methods require `owner`, `repo`, and `issue_number`. Get, update, and delete
also require `artifact_number`. Create requires `type`, `name`, and `body`;
an empty body is allowed. Update requires `name`, `body`, or both. Method-specific
fields are rejected when used with another method.

Create and list's optional type filter support the existing REST API types:
`generic`, `link`, `plan`, and `embedded_html`. For `embedded_html`, supply HTML
source in `body`. Returned artifact types remain unrestricted for forward
compatibility.

Get, create, and update return an `artifact` object. List returns a `list` object
containing `artifacts`, `page`, and `perPage`; delete returns a `deleted` object
containing `issue_number`, `artifact_number`, and a confirmation message.

Tools, resources, and prompts use `inventory.NewFeatureRule` when feature flags
change whether they are available. Each rule declares the flags it references
and evaluates them with a fail-closed `FeatureResolver`, so normal Go boolean
Expand Down Expand Up @@ -395,6 +432,30 @@ as output formatting) won't appear here.
- 'blocked_by' - the subject issue is blocked by the related issue.
- 'blocking' - the subject issue blocks the related issue. (string, required)

### `issue_artifacts`

- **issue_artifact_read** - Read issue artifacts
- **OAuth Challenge Scopes**: `repo`
- `artifact_number`: Required for get: positive issue-scoped artifact number, not its global ID (integer, optional)
- `issue_number`: Positive issue number (integer, required)
- `method`: (string, required)
- `owner`: Repository owner (string, required)
- `page`: For list only: page number, starting at 1 (default 1) (integer, optional)
- `perPage`: For list only: results per page, from 1 to 100 (default 30) (integer, optional)
- `repo`: Repository name (string, required)
- `type`: (string, optional)

- **issue_artifact_write** - Write issue artifacts
- **OAuth Challenge Scopes**: `repo`
- `artifact_number`: Required for update and delete: positive issue-scoped artifact number, not its global ID (integer, optional)
- `body`: Required for create, optional for update: artifact content (HTML source for embedded_html); an empty string clears it (string, optional)
- `issue_number`: Positive issue number (integer, required)
- `method`: (string, required)
- `name`: Required for create, optional for update: nonempty artifact name (string, optional)
- `owner`: Repository owner (string, required)
- `repo`: Repository name (string, required)
- `type`: (string, optional)

### `duplicate_detection`

- **find_duplicate** - Find duplicate issues
Expand Down
179 changes: 179 additions & 0 deletions pkg/github/__toolsnaps__/issue_artifact_read_ff_issue_artifacts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
{
"annotations": {
"destructiveHint": false,
"idempotentHint": false,
"readOnlyHint": true,
"title": "Read issue artifacts"
},
"description": "List or get GitHub issue artifacts including their bodies. Get requires artifact_number (issue-scoped, not its global ID). List accepts an optional type filter and page/perPage; request successive pages until a page contains fewer than perPage artifacts.",
"icons": [
{
"mimeType": "image/png",
"src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC",
"theme": "light"
},
{
"mimeType": "image/png",
"src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=",
"theme": "dark"
}
],
"inputSchema": {
"additionalProperties": false,
"properties": {
"artifact_number": {
"description": "Required for get: positive issue-scoped artifact number, not its global ID",
"minimum": 1,
"type": "integer"
},
"issue_number": {
"description": "Positive issue number",
"minimum": 1,
"type": "integer"
},
"method": {
"enum": [
"list",
"get"
],
"type": "string"
},
"owner": {
"description": "Repository owner",
"minLength": 1,
"type": "string",
"x-mcp-header": "owner"
},
"page": {
"description": "For list only: page number, starting at 1 (default 1)",
"minimum": 1,
"type": "integer"
},
"perPage": {
"description": "For list only: results per page, from 1 to 100 (default 30)",
"maximum": 100,
"minimum": 1,
"type": "integer"
},
"repo": {
"description": "Repository name",
"minLength": 1,
"type": "string",
"x-mcp-header": "repo"
},
"type": {
"enum": [
"generic",
"link",
"plan",
"embedded_html"
],
"type": "string"
}
},
"required": [
"owner",
"repo",
"issue_number",
"method"
],
"type": "object"
},
"name": "issue_artifact_read",
"outputSchema": {
"additionalProperties": false,
"properties": {
"artifact": {
"additionalProperties": false,
"description": "Artifact returned by get",
"properties": {
"body": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"number": {
"type": "integer"
},
"type": {
"type": "string"
}
},
"required": [
"id",
"number",
"type",
"name",
"body"
],
"type": [
"null",
"object"
]
},
"list": {
"additionalProperties": false,
"description": "Page of artifacts returned by list",
"properties": {
"artifacts": {
"items": {
"additionalProperties": false,
"properties": {
"body": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"number": {
"type": "integer"
},
"type": {
"type": "string"
}
},
"required": [
"id",
"number",
"type",
"name",
"body"
],
"type": "object"
},
"type": [
"null",
"array"
]
},
"page": {
"type": "integer"
},
"perPage": {
"type": "integer"
}
},
"required": [
"artifacts",
"page",
"perPage"
],
"type": [
"null",
"object"
]
}
},
"type": [
"null",
"object"
]
}
}
Loading
Loading