--- topic: issues source_keys: - gitea-mcp-repo - gitea-mcp-slim-go --- # Issue operations Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`, not copied from `api-reference.md` — this is deliberate: research docs are generated from source at a point in time and can drift from the server actually deployed. Last verified against gitea-mcp **v1.7.0**, as reported by `get_gitea_mcp_server_version`. Drift runs in both directions: this file previously recorded `list_issues` as having neither a `type` nor a `milestones` parameter, and v1.7.0 has both. Re-verify against the live schema if these tools appear to behave differently than documented here. ## `list_issues` **Parameters (live schema):** - `owner` (string, required) - `repo` (string, required) - `state` (string, optional, default `"all"`) — conventional values `"open"`/`"closed"`/`"all"`, not schema-enforced as an enum - `labels` (array of strings, optional) — filter by label *name* (not ID) - `milestones` (array of strings, optional) — filter by milestone name or numeric ID, both passed as strings - `type` (string, enum `"issues"` | `"pulls"`, optional) — omit it and the response mixes both - `since` (string, optional) — ISO 8601, issues updated after this time - `before` (string, optional) — ISO 8601, issues updated before this time - `page` (number, optional, default `1`) - `per_page` (number, optional, default `30`) **Pass `type: "issues"` on any listing meant to show issues.** Issues and PRs share one repo number space and the unfiltered response interleaves them; the only thing distinguishing them on a list item is the `html_url` path segment (`/issues/` vs `/pulls/`), since `is_pull` is not returned on list items — see the Gotchas section of SKILL.md. **Call:** ```text list_issues owner: repo: state: "open" type: "issues" ``` **Response (list item):** `number`, `title`, `state`, `html_url`, `user`, `comments`, `created_at`, `updated_at`, and optionally `labels` (`[]string`), `milestone` (`{id, title}`), `ref`, `deadline`. Body and `closed_at` are omitted from list responses — call `issue_read method: "get"` for those. Paginate: `page`/`per_page`, stop once the count is below `per_page`. ## `issue_read` **Parameters (live schema, matches `api-reference.md`):** - `method` (string, required, enum) — `"get"` | `"get_comments"` | `"get_labels"` - `owner` (string, required) - `repo` (string, required) - `issue_number` (number, required) **`get`** — full issue: `number`, `title`, `body`, `state`, `html_url`, `user`, `labels` (`[]string`), `comments`, `created_at`, `updated_at`, `closed_at`, and optionally `assignees` (`[]string`), `milestone` (`{id, title}`), `ref`, `deadline`, `is_pull` (present only when this number is backed by a pull request — absent, not `false`, on true issues). **`get_comments`** — array of `{id, body, user, html_url, created_at, updated_at}`. **`get_labels`** — array of full label objects (`id`, `name`, `color`, `description` — not slimmed to name strings, unlike the labels array on `get`). **Call:** ```text issue_read method: "get" owner: repo: issue_number: ``` Always check `is_pull` before treating a number as a plain issue — see the shared number-space gotcha in SKILL.md. ## `issue_write` **Parameters (live schema, matches `api-reference.md`):** - `method` (string, required, enum) — `"create"` | `"update"` | `"add_comment"` | `"edit_comment"` | `"add_labels"` | `"remove_label"` | `"replace_labels"` | `"clear_labels"` - `owner` (string, required) - `repo` (string, required) - `issue_number` (number, required for every method except `"create"`) - `title` (string, required for `"create"`) - `body` (string, required for `"create"`, `"add_comment"`, `"edit_comment"`) - `assignees` (array of strings, optional) — login names (see `references/enrichments.md` for why this is usually omitted) - `milestone` (number, optional) — milestone ID, never a title - `state` (string, enum `"open"`/`"closed"`/`"all"`, optional) — for `"update"` - `commentID` (number, optional, required for `"edit_comment"`) - `labels` (array of numbers, optional) — label IDs, never names — for `add_labels`/`replace_labels` - `label_id` (number, optional, required for `"remove_label"`) — singular, not the array form - `ref` (string, optional) — branch association, informational only - `deadline` (string, optional) — ISO 8601 - `remove_deadline` (boolean, optional) **Create:** ```text issue_write method: "create" owner: repo: title: body: <body> labels: [<resolved IDs>] ← omit if none confidently inferred milestone: <resolved ID> ← omit if none clearly fits assignees: ["<login>"] ← omit if no default configured ``` **Close:** ```text issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed" ``` No `method: "close"` exists — using one errors. **Comment:** ```text issue_write method: "add_comment" owner: <owner> repo: <repo> issue_number: <N> body: <text> ``` **Apply resolved label IDs directly** (bypassing `references/enrichments.md`'s inference step, e.g. when the caller already named exact labels): ```text issue_write method: "add_labels" owner: <owner> repo: <repo> issue_number: <N> labels: [<IDs>] ``` To replace all labels atomically instead of adding: `method: "replace_labels"`. To remove one: `method: "remove_label" label_id: <single ID>`. **Default to `add_labels`.** `replace_labels` clears every label not in the array, so it drops labels the caller never mentioned. Reach for it only when the caller asked for the issue's label set to become exactly what they listed. In particular, do not use it to enforce one-label-per-scope: exclusivity is a per-label property — the server drops the sibling itself for a label whose `exclusive` field is `true`, and a label whose `exclusive` is `false` (every `Kind/*` on this instance) is legitimately stackable. See `gitea-labels-milestones` for how to read that field. ## Token scope All of `list_issues`, `issue_read`, and `issue_write` are verified working under a token holding `write:issue` + `write:repository`.