gitea-files' always-loaded Gotchas said "content is base64 both ways" without qualification. The `main` text carried an exception for `withLines: true` and both halves were dropped. Verified live: `get_file_contents` with `withLines: true` returns plain JSON text while the same response still reports `"encoding":"base64"`. An agent that follows the recommendation two sentences later and applies the unconditional decode gets garbage, with the response's own field confirming the wrong answer. Exception restored, and the lying field named. gitea-orchestrate was never updated for `rename_branch`: absent from the operation enum, so an agent caller got "unknown operation", and absent from the destructive-confirm list, though branches.md requires a rename with open PRs or a protection rule to be confirmed exactly as `delete_branch` is. Added to both — the confirm gate rather than the enum alone, because accepting the operation without it routes around a rule the skill states while appearing to support it. The compatibility frontmatter, which the agent reads, still omitted the tool too. Two more always-loaded Gotchas contradicted their own reference files, and the Gotcha was wrong both times: issues and PRs are distinguishable on a list item by the `html_url` path segment (confirmed live — #129 at /pulls/, #128 at /issues/), and `get_repository_tree` takes `tree_sha`, not `ref`. `review_comments` was asserted as unconditionally present on the PR get response. It is absent on a PR with no review comments, so the claim is downgraded to present-when-non-zero rather than stated as response shape. The label-exclusivity relocation moved the rule out of label-inference.md and into labels.md without updating sources.md, leaving the one rule in this branch that writes differently to live repos citing a file that no longer carries it. The rule itself is correct as it stands and `main` was wrong — every Kind/* label on this instance is exclusive:false, every Priority/* and Status/* is true — so only the provenance record is corrected. Routing: gitea-workflow lost the human-caller discriminator and widened from status checks to any request, which sent "close #42" to a branch that resolves the number and presents detail without ever closing it. gitea-branches and gitea-issues regain trigger phrasings the retrofit dropped. Refs: #92
123 lines
5.3 KiB
Markdown
123 lines
5.3 KiB
Markdown
---
|
|
topic: labels
|
|
source_keys:
|
|
- gitea-mcp-repo
|
|
- gitea-mcp-slim-go
|
|
- context7-websites-gitea
|
|
---
|
|
|
|
# Label operations
|
|
|
|
Execution detail for `label_read` and `label_write`. Both tools operate on either **repo-scoped**
|
|
or **org-scoped** labels — never both in one call. Pick the method family (`*_repo_label*` vs.
|
|
`*_org_label*`) that matches the target, and pass `owner`+`repo` or `org` accordingly.
|
|
|
|
## Verified live schemas
|
|
|
|
`label_read` — required: `method`.
|
|
|
|
| Param | Type | Notes |
|
|
|---|---|---|
|
|
| `method` | string (enum) | `"list_repo_labels"` \| `"get_repo_label"` \| `"list_org_labels"` |
|
|
| `owner` | string | for repo methods |
|
|
| `repo` | string | for repo methods |
|
|
| `org` | string | for org methods |
|
|
| `id` | number | label ID, required for `"get_repo_label"` |
|
|
| `page` | number | default `1` |
|
|
| `per_page` | number | default `30` |
|
|
|
|
`label_write` — required: `method`.
|
|
|
|
| Param | Type | Notes |
|
|
|---|---|---|
|
|
| `method` | string (enum) | `"create_repo_label"` \| `"edit_repo_label"` \| `"delete_repo_label"` \| `"create_org_label"` \| `"edit_org_label"` \| `"delete_org_label"` |
|
|
| `owner` | string | for repo methods |
|
|
| `repo` | string | for repo methods |
|
|
| `org` | string | for org methods |
|
|
| `id` | number | for edit/delete |
|
|
| `name` | string | required for create |
|
|
| `color` | string | hex `#RRGGBB`, required for create |
|
|
| `description` | string | optional |
|
|
| `exclusive` | boolean | accepted as a *write* param on org creates only — repo labels still carry and enforce `exclusive`, set outside this tool surface |
|
|
| `is_archived` | boolean | repo labels only |
|
|
|
|
Note: unlike `milestone_read`/`milestone_write`, `owner`/`repo`/`org` are **not** schema-required on
|
|
either label tool — only `method` is. Passing none for a repo/org method still fails, just as a
|
|
runtime error from Gitea rather than a client-side validation error.
|
|
|
|
## List repo labels
|
|
|
|
```text
|
|
label_read method: "list_repo_labels" owner: <owner> repo: <repo> per_page: 50
|
|
```
|
|
|
|
Paginate (`page: 1, 2, ...`) until the returned count is less than `per_page`. This is the only way
|
|
to build a complete name → ID map — there is no lookup-by-name endpoint.
|
|
|
|
Every returned repo label carries its own `exclusive` boolean, so exclusivity is always *readable*
|
|
per repo label. That does not contradict `label_write`'s schema, which annotates `exclusive` as
|
|
"(org only)": reading and setting are different questions, and only the setting half is org-scoped
|
|
(see "Create a label" below). Where the field is `true` Gitea enforces one-label-per-scope
|
|
server-side; where it is `false` labels in that scope stack legitimately. Read the field — never
|
|
infer exclusivity from the `/` in a name, and never carry another repo's map over.
|
|
|
|
On the instance this skill was authored against (`Defame1297/holocron`) the split ran: every
|
|
`Priority/*`, `Reviewed/*` and `Status/*` label `exclusive: true`, every `Kind/*` label and
|
|
`Compat/Breaking` `exclusive: false`. That is one repo's configuration at one point in time, recorded
|
|
as a worked example of what the field looks like in practice — it is not a property of the taxonomy
|
|
and says nothing about the repo you are called against.
|
|
|
|
## Get one label
|
|
|
|
```text
|
|
label_read method: "get_repo_label" owner: <owner> repo: <repo> id: <id>
|
|
```
|
|
|
|
## Resolve a name to an ID
|
|
|
|
The tool surface carries no lookup-by-name method. List all repo labels (paginating if needed),
|
|
scan for a case-insensitive name match, and extract `id`. Both pools can apply to one issue: if the
|
|
name is not in `list_repo_labels`, also check `list_org_labels` before reporting it unresolved. That method
|
|
takes `org`, not `owner`/`repo` — pass the repo's `owner` as `org`, which is what it means when the
|
|
owner is an organisation. Its failure modes are not interchangeable. `token does not have at least
|
|
one of required scope(s), required=[read:organization]` means the org pool was never queried — report
|
|
that missing scope rather than reporting the label unresolved. Only a not-found response means the
|
|
owner is a user account with no org pool, making the miss a real miss.
|
|
|
|
Resolution is the required first step before any label application on an issue or PR — the actual
|
|
`add_labels`/`replace_labels`/`remove_label` call lives in `gitea-issues`/`gitea-prs` via
|
|
`issue_write`/`pull_request_write`, which take numeric IDs only.
|
|
|
|
## Create a label
|
|
|
|
```text
|
|
label_write method: "create_repo_label"
|
|
owner: <owner> repo: <repo>
|
|
name: "Kind/Bug"
|
|
color: "#d73a4a"
|
|
description: "Confirmed bug"
|
|
```
|
|
|
|
For an org label, use `method: "create_org_label"` with `org:` instead of `owner`/`repo`, and
|
|
`exclusive: true` if the label belongs to a mutually-exclusive scope group. `label_write` accepts
|
|
`exclusive` on org methods only, so a repo label's exclusivity cannot be set or cleared through this
|
|
tool — it is set in the Gitea UI or against the REST API directly, and read back via
|
|
`list_repo_labels`.
|
|
|
|
## Edit a label
|
|
|
|
```text
|
|
label_write method: "edit_repo_label" owner: <owner> repo: <repo> id: <id> color: "#ff0000"
|
|
```
|
|
|
|
Only pass the fields being changed — `id` plus any of `name`/`color`/`description`/`is_archived`.
|
|
|
|
## Delete a label
|
|
|
|
```text
|
|
label_write method: "delete_repo_label" owner: <owner> repo: <repo> id: <id>
|
|
```
|
|
|
|
Deleting a label does not remove it from historical issue/PR timeline events — it disappears only
|
|
from current label lists.
|