fix(gitea): make gitea-releases executable and correct misleading domain claims

gitea-releases was the weakest skill in the plugin: no allowed-tools, no
owner/repo resolution, and a checkbox list where a dispatch table belongs, so
an agent reaching it had to guess both its permissions and its inputs. The
id-vs-tag_name trap — deleting by tag name where the API wants the numeric id —
is restored as an explicit Gotcha because it destroys the wrong release
silently.

Elsewhere the `exclusive` flag was documented on the wrong side of the
read/write split, and label data from one instance was presented as though it
were universal, which invites an agent to assume a taxonomy that does not
exist on the target repo. rename_branch was missing from the branch surface.
Reference prose and fences are cleaned up in passing.
This commit is contained in:
2026-08-31 08:01:33 +00:00
parent b07d54ad7a
commit 8680adf4c0
52 changed files with 408 additions and 238 deletions

View File

@@ -4,7 +4,7 @@ Manage Gitea repository branches and inspect commit history via the Gitea MCP se
## What it does
This skill handles branch lifecycle operations (list, create, delete) and read-only commit
This skill handles branch lifecycle operations (list, create, rename, delete) and read-only commit
history (list commits, get a single commit's full detail) against a Gitea repository. It resolves
`owner`/`repo` from the git remote, dispatches to the right MCP tool, and applies safety and
pagination conventions specific to Gitea's API (e.g. refusing to delete a protected branch without
@@ -18,25 +18,26 @@ Branch references that only exist relative to a pull request — a PR's head or
cross-repo fork PR heads in particular — belong to `gitea-prs`; `list_branches` cannot see a fork's
head at all.
The skill triggers on phrasings like "list branches", "create a branch", "delete a branch",
The skill triggers on phrasings like "list branches", "create a branch", "rename a branch", "delete a branch",
"what commits are on this branch", "show commit <sha>", and "what changed in that commit", even
when the user does not say "Gitea", as long as the repo's remote is a Gitea instance.
## Before you start
Requires a Gitea MCP server configured with a token that has `write:repository` scope. This is
confirmed for `list_branches`, `create_branch`, and `delete_branch` (Gitea gates reads behind write
confirmed for `list_branches`, `create_branch`, and `delete_branch` (and inferred for
`rename_branch`) (Gitea gates reads behind write
scope for repo-scoped operations); `list_commits` and `get_commit` are inferred to need the same
scope by analogy, not explicitly confirmed — see `references/commits.md`. Requires a git remote
named `origin` pointing at the Gitea instance.
## Usage
```
```text
/gitea-branches
```
Describe your task: list/create/delete a branch, or list/inspect commits. See `SKILL.md`'s
Describe your task: list/create/rename/delete a branch, or list/inspect commits. See `SKILL.md`'s
dispatch table for the full set of recognized invocations.
## Files
@@ -44,6 +45,6 @@ dispatch table for the full set of recognized invocations.
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents — dispatch table, gotchas |
| `references/branches.md` | Verified call signatures and mechanics for list/create/delete branch |
| `references/branches.md` | Verified call signatures and mechanics for list/create/rename/delete branch |
| `references/commits.md` | Verified call signatures and mechanics for list/get commit |
| `references/sources.md` | Research sources backing the branch/commit guidance |

View File

@@ -2,10 +2,10 @@
name: gitea-branches
description: >
Use when listing, creating, or deleting branches in a Gitea repository, or
reading its commit history — even when the user does not say "Gitea". Not a
local working copy's branches -> `git-branches`. Not local history ->
`git-history`. Not a PR's head or base branch -> `gitea-prs`.
Use when listing, creating, renaming, or deleting branches in a Gitea repository,
or reading its commit history — even when the user does not say "Gitea". Not a
local checkout's branches -> `git-branches`. Not local history ->
`git-history`. Not a PR's head or base -> `gitea-prs`.
compatibility: Requires Gitea MCP server configured with a token with write:repository scope; this is confirmed to gate list_branches, create_branch, and delete_branch (Gitea gates reads behind write scope for repo-scoped operations), and is inferred by analogy (not explicitly confirmed by source docs) to also gate list_commits and get_commit. Requires git remote "origin" pointing to the Gitea instance.
@@ -17,7 +17,7 @@ metadata:
- gitea-mcp-slim-go
- context7-websites-gitea
allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit
allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__gitea__rename_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit
---
## Gotchas
@@ -42,17 +42,18 @@ git remote get-url origin
|---|---|
| `/gitea-branches` or `/gitea-branches list` | List branches |
| `/gitea-branches create <name> [from <base>]` | Create branch |
| `/gitea-branches rename <name> to <new-name>` | Rename branch |
| `/gitea-branches delete <name>` | Delete branch |
| `/gitea-branches commits [on <branch>] [touching <path>]` | List commit history |
| `/gitea-branches commit <sha>` | Get full detail for one commit |
For branch operations (list/create/delete), read `references/branches.md` — it carries the call signatures, the `old_branch` source rule, and the protected-branch refusal in full.
For branch operations (list/create/rename/delete), read `references/branches.md` — it carries the call signatures, the `old_branch` source rule, and the protected-branch refusal in full.
For commit operations (list/get), read `references/commits.md`.
## Step 3 — Report
For reads: display branches as name + protected flag; display commits as SHA (short), message summary, author, date.
For writes (create/delete): confirm the action taken, the branch name, and (for create) the base it forked from.
For writes (create/rename/delete): confirm the action taken, the branch name, and (for create) the base it forked from or (for rename) the name it had before.
For errors: surface the HTTP code and message, applying the 404 gotcha above before reporting "not found" to the user.

View File

@@ -7,10 +7,11 @@ source_keys:
# Branch operations
Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`
at authoring time, not copied from research docs — this is deliberate: research docs are generated
from source code at a point in time and can drift from the server actually deployed. Re-verify
against the live schema if these tools appear to behave differently than documented here.
Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`,
not copied from research docs — this is deliberate: research docs are generated from source code 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`. Re-verify against the live schema if the
deployed version differs or these tools behave differently than documented here.
## `list_branches`
@@ -21,7 +22,7 @@ against the live schema if these tools appear to behave differently than documen
- `per_page` (number, optional, default: `30`)
**Call:**
```
```text
list_branches owner: <owner> repo: <repo>
```
@@ -41,7 +42,7 @@ count is less than `per_page`.
branch server-side (not necessarily your current local checkout)
**Call:**
```
```text
create_branch owner: <owner> repo: <repo> branch: <new-name> old_branch: <source-branch>
```
@@ -53,6 +54,29 @@ top-level request with no working branch context), omit `old_branch` and let it
A branch name collision returns `409 Conflict`.
## `rename_branch`
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `branch` (string, required) — the branch's current name
- `new_name` (string, required) — the name to move it to
**Call:**
```text
rename_branch owner: <owner> repo: <repo> branch: <current-name> new_name: <new-name>
```
A rename moves the ref server-side; it is not a delete-plus-create, and no commit history is
rewritten. What it does to things *pointing at* the old name — open pull requests using it as head or
base, a branch protection rule matching it, CI config, and tracking branches on every other clone —
is **not confirmed** by this skill's sources: the deployed tool describes itself only as "Rename an
existing branch in a repository". Treat a rename of a branch with open PRs or a protection rule as a
change needing verification afterward (`list_branches`, plus `gitea-prs` for the PR side), and
confirm with the user first, exactly as for `delete_branch` below. A collision with an existing
branch name is expected to return `409 Conflict` by analogy with `create_branch`, not separately
confirmed.
## `delete_branch`
**Parameters:**
@@ -61,7 +85,7 @@ A branch name collision returns `409 Conflict`.
- `branch` (string, required)
**Call:**
```
```text
delete_branch owner: <owner> repo: <repo> branch: <name>
```
@@ -73,9 +97,12 @@ protected, every time, regardless of how the request is phrased.
## Token scope
All three — `list_branches`, `create_branch`, `delete_branch` — require `write:repository`. Gitea
`list_branches`, `create_branch` and `delete_branch` all require `write:repository`. Gitea
gates reads behind write scope for repo-scoped operations, so `list_branches` needs the same scope
as the write operations, not `write:issue` alone. An earlier version of this doc claimed
`write:issue` alone was sufficient for `list_branches`, based on empirical testing under a token
that held both `write:issue` and `write:repository` simultaneously — that test didn't isolate the
variable, so it couldn't actually establish `write:issue` alone as sufficient.
`rename_branch` is a write on the same repo-scoped surface and is inferred to need `write:repository`
too — inferred by analogy, not separately confirmed.

View File

@@ -8,8 +8,9 @@ source_keys:
# Commit operations
Read-only commit history, scoped to a repo (optionally to one branch or one path). Call signatures
below were verified live against the deployed `gitea-mcp` server via `ToolSearch` at authoring time,
not copied from research docs, for the same drift-avoidance reason noted in `references/branches.md`.
below were verified live against the deployed `gitea-mcp` server via `ToolSearch`, not copied from
research docs, for the same drift-avoidance reason noted in `references/branches.md`. **Last verified
against gitea-mcp v1.7.0**, as reported by `get_gitea_mcp_server_version`.
This domain has no prior skill precedent — it's new coverage added alongside branches because commit
history is naturally scoped to a branch (a "what happened on this branch" question), not because it
@@ -27,7 +28,7 @@ shares any tool family with branch create/delete.
- `per_page` (number, optional, default: `30`, minimum: `1`)
**Call:**
```
```text
list_commits owner: <owner> repo: <repo> sha: <branch-or-sha> path: <optional-path>
```
@@ -52,7 +53,7 @@ Paginate per the pagination Gotcha in SKILL.md if you need more than one page of
- `sha` (string, required)
**Call:**
```
```text
get_commit owner: <owner> repo: <repo> sha: <commit-sha>
```

View File

@@ -2,8 +2,8 @@
**Note on call signatures:** per `docs/adr/0011-gitea-skill-deep-modules.md`, the tool parameter
signatures in `references/branches.md` and `references/commits.md` were re-verified live via
`ToolSearch` against the deployed `gitea-mcp` server at authoring time — they are not copied
verbatim from `api-reference.md` below. This resolves issue #6 comment #849's root-cause finding
`ToolSearch` against the deployed `gitea-mcp` server — **last verified against v1.7.0**, as reported
by `get_gitea_mcp_server_version` — rather than copied verbatim from `api-reference.md` below. This resolves issue #6 comment #849's root-cause finding
that a prior skill was authored from API docs that had drifted from the actual MCP tool schema.
The research docs cited here informed gotchas, response shapes, and workflow context, not the
parameter lists themselves.
@@ -11,7 +11,7 @@ parameter lists themselves.
## gitea-mcp-repo
- **URL:** https://gitea.com/gitea/gitea-mcp
- **Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags. Informed the dispatch table and pagination / 404-may-mean-403 gotchas in SKILL.md, and the list/create/delete branch and list/get commit mechanics (including 409 conflict and default-branch fallback behavior) in references/branches.md and references/commits.md.
- **Description:** Official gitea-mcp repository; operation/*.go source files documenting the MCP tools, their parameters, and CLI flags. Extracted at v1.3.0; the parameter lists carried into this skill are re-verified live against the deployed server, last at v1.7.0. Informed the dispatch table and pagination / 404-may-mean-403 gotchas in SKILL.md, and the list/create/delete branch and list/get commit mechanics (including 409 conflict and default-branch fallback behavior) in references/branches.md and references/commits.md.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md, references/branches.md, references/commits.md
- **Status:** `extracted`
@@ -19,7 +19,7 @@ parameter lists themselves.
## gitea-mcp-slim-go
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/repo/slim.go
- **Description:** Slim response shape structs from gitea-mcp source; defines exactly which fields the MCP server returns for branches (name, protected, commit_sha) and commits (sha, html_url, created, message, author), and informed get_commit's always-populated guarantee vs. list_commits' conditional fields.
- **Description:** Slim response shape structs from gitea-mcp source, extracted at v1.3.0; defines exactly which fields the MCP server returns for branches (name, protected, commit_sha) and commits (sha, html_url, created, message, author), and informed get_commit's always-populated guarantee vs. list_commits' conditional fields.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** references/branches.md, references/commits.md
- **Status:** `extracted`

View File

@@ -8,7 +8,7 @@ This skill handles file-domain operations within the Gitea integration suite: re
## Usage
```
```text
/gitea-files
```

View File

@@ -24,7 +24,7 @@ allowed-tools: mcp__gitea__get_file_contents mcp__gitea__get_dir_contents mcp__g
- **A 404 may mean an under-scoped token, not a missing path.** Every tool here gates on `write:repository`, and Gitea masks insufficient scope as 404. Check scopes first.
- **Reads take `ref`, writes take `branch_name`.** One concept, two parameter names — chaining a read into a write drops the branch if you carry the wrong key.
- **`content` is base64 both ways.** Encode before a write, decode after a read; `withLines: true` returns numbered lines.
- **`content` is base64 both ways.** Encode before a write, decode after a read.
## Inputs
@@ -32,14 +32,12 @@ allowed-tools: mcp__gitea__get_file_contents mcp__gitea__get_dir_contents mcp__g
## Dispatch
Read the reference for the row you land on before making the call.
| Condition | Flow | Reference |
|---|---|---|
| Read one file, list one directory level, or walk the repository tree | Read | `references/reading.md` |
| Create, update, or delete a file | Write | `references/writing.md` |
If the request only inspects repository contents, read `references/reading.md` — it carries the three read tools, their pagination behaviour, and why neither a directory listing nor a tree entry supplies the SHA a write needs.
If the request creates, updates or deletes a file, read `references/writing.md` — it carries the SHA-first sequence every update and delete depends on, the worked multi-call sequence, and how to triage a write that fails.
| Read one file, list one directory level, or walk the repository tree | Read | `references/reading.md` — the three read tools, their pagination behaviour, and why neither a directory listing nor a tree entry supplies the SHA a write needs |
| Create, update, or delete a file | Write | `references/writing.md` — the SHA-first sequence every update and delete depends on, the worked multi-call sequence, and how to triage a write that fails |
A request that reads and then writes runs both flows in that order: fetch the file first, then write with the SHA that call returned.

View File

@@ -29,15 +29,17 @@ commit lands on it in one call, replacing a separate branch-creation step.
## Delete a file
Same SHA-first pattern, with no create-style fallback — `delete_file` without `sha` returns
HTTP 422.
Same SHA-first pattern, with no create-style fallback. `sha` is schema-**required** on
`delete_file`, unlike `create_or_update_file` where omitting it means *create* — so an omitted `sha`
is rejected client-side by input validation and the call never reaches Gitea. The HTTP 422 that is
actually reachable here is the stale-`sha` case.
1. `get_file_contents(owner, repo, ref: <branch>, path)` → read the top-level `sha`.
2. `delete_file(owner, repo, path, message, branch_name, sha: <that value>)`.
## Worked sequence — new file on a new branch, then a PR
```
```text
1. create_or_update_file
owner, repo
path: "docs/example.md"
@@ -58,9 +60,10 @@ when the write replaces an existing one.
| Symptom | Cause | Action |
|---|---|---|
| HTTP 409 | `sha` omitted on a path that already exists | Fetch the current SHA, retry as an update |
| HTTP 422 | `sha` missing or stale | Re-fetch the SHA immediately before the write |
| HTTP 422 | Stale `sha` — the file changed between the read and the write | Re-fetch the SHA immediately before the write |
| 403 or 422 with no SHA explanation | Branch protection requires signed commits | Stop and report |
| HTTP 413 | Reverse-proxy body limit in front of Gitea | Report; retrying cannot fix it |
| Client-side input-validation error naming `sha` | `sha` omitted on `delete_file`, where it is schema-required | Fetch the current SHA and retry — nothing was sent to Gitea |
| HTTP 404 | Wrong path, or a token without `write:repository` | Verify the path, then the token's scopes |
**Signed commits.** These writes create commits server-side from a bare API token with no 2FA or

View File

@@ -10,9 +10,8 @@ its state, adding/editing comments, applying labels, and searching issues/PRs ac
The create flow closes out four enrichments deferred from issue #6 comment #848: label inference
and milestone assignment (both by composing `gitea-labels-milestones`), an assignee workaround for
the blocked `get_me` scope, and the "Depends on #N" dependency-linking convention. It supersedes the
`issue`/`issue <N>`/`issue close <N>`/`issue comment <N>` dispatch in the old flat
`plugins/bin/skills/gitea/SKILL.md`, removed per
`docs/adr/0011-gitea-skill-deep-modules.md`.
`issue`/`issue <N>`/`issue close <N>`/`issue comment <N>` dispatch this plugin's old single flat
Gitea skill carried, retired when the plugin was split into per-domain deep modules.
## Before you start
@@ -34,7 +33,7 @@ issue request: local git branch or commit work belongs to `gitea-branches` (Gite
## Usage
```
```text
/gitea-issues
```

View File

@@ -84,8 +84,8 @@ repo-scoped number space. Use the bare `#N` form for same-repo dependencies; use
a dependency in a different repo.
When creating an issue that depends on another, append a line like:
```
```text
Depends on #42
```
to the body before calling `issue_write method: "create"`. There is no separate field or follow-up
call — the rendering happens automatically once the body is saved.
to the body before calling `issue_write method: "create"`. No separate field or follow-up call is
involved — the rendering happens automatically once the body is saved.

View File

@@ -37,7 +37,7 @@ item is the `html_url` path segment (`/issues/` vs `/pulls/`), since `is_pull` i
list items — see the Gotchas section of SKILL.md.
**Call:**
```
```text
list_issues owner: <owner> repo: <repo> state: "open" type: "issues"
```
@@ -66,7 +66,7 @@ number is backed by a pull request — absent, not `false`, on true issues).
to name strings, unlike the labels array on `get`).
**Call:**
```
```text
issue_read method: "get" owner: <owner> repo: <repo> issue_number: <N>
```
@@ -95,7 +95,7 @@ gotcha in SKILL.md.
- `remove_deadline` (boolean, optional)
**Create:**
```
```text
issue_write method: "create"
owner: <owner> repo: <repo>
title: <title> body: <body>
@@ -105,19 +105,19 @@ issue_write method: "create"
```
**Close:**
```
```text
issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed"
```
There is no `method: "close"` — using one will error.
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"`.

View File

@@ -22,12 +22,12 @@ time (see `references/sources.md`) — confirmed to match `api-reference.md`.
- `per_page` (number, optional, default `30`)
**Call:**
```
```text
search_issues query: <text>
```
**Narrowing the search:**
```
```text
search_issues query: <text> owner: <owner> state: "open" type: "pulls" labels: "bug,urgent"
```

View File

@@ -14,7 +14,7 @@ That relationship is documented here rather than in the skill description, which
## Usage
```
```text
/gitea-labels-milestones
```

View File

@@ -24,7 +24,7 @@ allowed-tools: Bash mcp__gitea__label_read mcp__gitea__label_write mcp__gitea__m
- **Applying a label takes a numeric ID, but issue/PR responses slim labels down to name strings.** An issue's existing labels yield no IDs — resolve name → ID with `label_read`.
- **`pull_request_read` returns `milestone` as a bare title string** where `issue_read` returns `{id, title}` — recover the milestone's ID by listing milestones and matching the title.
- **Never assume a `Kind/*`/`Priority/*`/`Status/*` scope is exclusive — read each label's own `exclusive` field.** `list_repo_labels` returns it per repo label, it is not org-only, and where it is `true` Gitea enforces one-per-scope itself. Replacing rather than stacking on a label whose `exclusive` is `false` destroys a valid label.
- **Never assume a `Kind/*`/`Priority/*`/`Status/*` scope is exclusive — read each label's own `exclusive` field.** `list_repo_labels` returns it on every repo label, so it is always *readable* per label; `label_write` documents it as "(org only)" because it is only *settable* through the org create methods. Where it is `true` Gitea enforces one-per-scope itself, and replacing rather than stacking on a label whose `exclusive` is `false` destroys a valid label.
## Step 1 — Resolve owner, repo and org

View File

@@ -12,15 +12,11 @@ description) to this repo's `Kind/*` / `Priority/*` / `Status/*` label taxonomy.
`gitea-issues` and `gitea-prs` before creating or updating an issue/PR, and directly when the user
asks to label something without naming exact labels.
## Exclusivity is per label — read it, never infer it
## Branching on exclusivity
Gitea's `exclusive` flag is a real per-label boolean returned by `list_repo_labels`, and where it is
`true` the server enforces one-label-per-scope itself. It is not an org-only setting, and the `/`
delimiter in a name says nothing about it. Verified on `Defame1297/holocron`: every `Priority/*`,
`Reviewed/*` and `Status/*` label is `exclusive: true`, while every `Kind/*` label — and
`Compat/Breaking` — is `exclusive: false` and is used stacked.
So read each candidate label's own `exclusive` value from the resolution call and branch on it:
`references/labels.md` owns the exclusivity rule and the read-versus-write asymmetry behind it. Read
it there rather than assuming a scope's behaviour from its name. Inference needs only the branch:
carry each candidate label's own `exclusive` value forward from the resolution call and act on it.
- **`exclusive: true`** — the server drops the sibling on write. Add the label and let it; do not
pre-remove the label already there, and do not compute a replacement set client-side. Inferring
@@ -29,7 +25,7 @@ So read each candidate label's own `exclusive` value from the resolution call an
same scope destroys a valid one: an issue can legitimately carry `Kind/Bug` and `Kind/Security`
at once.
There is no client-side exclusivity convention for this skill to enforce.
This skill enforces no client-side exclusivity convention of its own.
## Signal → label mapping
@@ -42,6 +38,7 @@ There is no client-side exclusivity convention for this skill to enforce.
| Improvement to existing behavior, "make X better", refactor with behavior change | `Kind/Enhancement` |
| Docs-only change, README/comment/guide updates | `Kind/Documentation` |
| Vulnerability, credential exposure, injection risk, auth bypass | `Kind/Security` |
| Test coverage, "add tests for X", a missing or flaky test, a test-only change | `Kind/Testing` |
**`Priority/*`** (urgency):
@@ -49,6 +46,7 @@ There is no client-side exclusivity convention for this skill to enforce.
|---|---|
| "blocking", "critical", "urgent", production-down | `Priority/Critical` |
| "soon", "high priority", "should do this sprint" | `Priority/High` |
| "low priority", "nice to have", "whenever", explicitly deferred | `Priority/Low` |
| No urgency signal present | `Priority/Medium` (default) |
**`Status/*`** (workflow state):
@@ -57,6 +55,10 @@ There is no client-side exclusivity convention for this skill to enforce.
|---|---|
| Explicit statement that the work is blocked on something else | `Status/Blocked` |
The label names in all three tables are the taxonomy this guide was written against; none of them is
guaranteed to exist on the target repo. Step 2 below resolves every inferred name against the live
label set, and a name that does not resolve is reported rather than substituted.
## Procedure
1. Read the conversation context (issue/PR title, body, or the triggering discussion) for the

View File

@@ -46,30 +46,37 @@ 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; the field is not org-only. Verified on
`Defame1297/holocron`: all `Priority/*`, `Reviewed/*` and `Status/*` labels are `exclusive: true`,
while all `Kind/*` labels and `Compat/Breaking` are `exclusive: false`. Where it 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.
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
There is no direct name lookup. 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
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
@@ -82,7 +89,7 @@ Resolution is the required first step before any label application on an issue o
## Create a label
```
```text
label_write method: "create_repo_label"
owner: <owner> repo: <repo>
name: "Kind/Bug"
@@ -98,7 +105,7 @@ tool — it is set in the Gitea UI or against the REST API directly, and read ba
## Edit a label
```
```text
label_write method: "edit_repo_label" owner: <owner> repo: <repo> id: <id> color: "#ff0000"
```
@@ -106,7 +113,7 @@ Only pass the fields being changed — `id` plus any of `name`/`color`/`descript
## Delete a label
```
```text
label_write method: "delete_repo_label" owner: <owner> repo: <repo> id: <id>
```

View File

@@ -43,7 +43,7 @@ schema level — there's no scope variant to omit them for.
## List milestones
```
```text
milestone_read method: "list" owner: <owner> repo: <repo> state: "open"
```
@@ -51,7 +51,7 @@ Report each as: id, title, state, due date, open/closed issue counts.
## Get one milestone
```
```text
milestone_read method: "get" owner: <owner> repo: <repo> id: <id>
```
@@ -60,7 +60,7 @@ milestone_read method: "get" owner: <owner> repo: <repo> id: <id>
Needed whenever the only handle available is a title — e.g. a `pull_request_read` response, which
returns `milestone` as a bare title string rather than `{id, title}`. Call:
```
```text
milestone_read method: "list" owner: <owner> repo: <repo> name: <title>
```
@@ -69,7 +69,7 @@ title typo or case mismatch), fall back to listing without the filter and matchi
## Create a milestone
```
```text
milestone_write method: "create"
owner: <owner> repo: <repo>
title: "v1.0"
@@ -82,7 +82,7 @@ this milestone via `issue_write`/`pull_request_write`.
## Update or close a milestone
```
```text
milestone_write method: "update" owner: <owner> repo: <repo> id: <id> state: "closed"
```
@@ -90,7 +90,7 @@ Only pass the fields being changed — `id` plus any of `title`/`description`/`d
## Delete a milestone
```
```text
milestone_write method: "delete" owner: <owner> repo: <repo> id: <id>
```

View File

@@ -8,11 +8,17 @@ This skill handles the pull request lifecycle within the Gitea integration suite
## Usage
```
```text
/gitea-prs
```
Describe the PR or review task: list PRs, get a PR's status/diff/reviews, create or update a PR, merge one, or create/submit/dismiss a code review. The skill will determine owner/repo from context and resolve any label or milestone names via `gitea-labels-milestones` before writing them.
Describe the PR or review task: list PRs, get a PR's status/diff/reviews, create or update a PR, merge one, or create/submit/dismiss a code review. The skill resolves `owner`/`repo` from the `origin` git remote (or takes them from an orchestrating caller) and resolves any label or milestone names via `gitea-labels-milestones` before writing them.
## Before you start
Requires a Gitea MCP server configured with a token holding `write:issue` + `write:repository`.
Requires a git remote named `origin` pointing at the Gitea instance, unless an orchestrating caller
(e.g. `gitea-workflow`) already resolved `owner`/`repo` for you.
## Files

View File

@@ -5,7 +5,10 @@ description: >
Use when listing, reading, creating, updating, merging, or reviewing Gitea pull requests — even
when the user does not say "Gitea". Not issues -> `gitea-issues`.
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token scopes.
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token
scopes. Requires git remote "origin" pointing to the Gitea instance for owner/repo resolution when
invoked directly by a human; an orchestrating caller (e.g. gitea-workflow) may pass owner/repo
already resolved.
metadata:
category: integration
@@ -16,7 +19,7 @@ metadata:
- context7-gitea-tea-cli
version: "0.1.2"
allowed-tools: mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp__gitea__pull_request_write mcp__gitea__pull_request_review_write
allowed-tools: Bash mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp__gitea__pull_request_write mcp__gitea__pull_request_review_write
---
## Gotchas
@@ -24,9 +27,19 @@ allowed-tools: mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp_
- **Issues and PRs share one number space.** `#42` may be an issue rather than a PR. When unsure, call `pull_request_read method: "get"` and read a 404 as "that number is an issue" — hand it to `gitea-issues`.
- **`pull_request_write method: "create"` discards most optional parameters in silence.** `milestone`, `assignee`, `assignees`, `reviewers` and `team_reviewers` are accepted, dropped, and left out of the response, so a drop is indistinguishable from never passing them. `labels` *does* apply on `"create"`, so labels landing is no evidence the milestone did.
## Dispatch
## Step 1 — Resolve owner and repo
Resolve `owner` and `repo` from context first, and confirm the number names a PR before writing to it.
Extract them from the git remote before any tool call, skipping this when an orchestrating caller already passed them in:
```bash
git remote get-url origin
```
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
## Step 2 — Dispatch
Confirm the number names a PR, not an issue, before writing to it.
| Task | Tool | Reference |
|---|---|---|
@@ -36,11 +49,9 @@ Resolve `owner` and `repo` from context first, and confirm the number names a PR
| Merge a PR, or judge whether it can merge | `pull_request_write method: "merge"` | `references/merging.md` |
| Read, create, submit, dismiss or delete a code review, or reply to and resolve a review comment thread | `pull_request_read`, `pull_request_review_write` | `references/reviews.md` |
Whatever `"create"` dropped takes a second call once the PR exists — `"update"` for milestone and assignees, `"add_reviewers"` for reviewers.
Read the reference for the row you land on before making the call. Each carries the parameter signatures, the per-method behaviour and the response-shape quirks the row cannot, and every write method has at least one parameter that behaves differently from its issue-side counterpart.
## Resolving labels and milestones
## Step 3 — Resolving labels and milestones
`labels` and `milestone` take numeric IDs, never name or title strings. Before a `pull_request_write` call carrying either, resolve them through `gitea-labels-milestones`: `label_read method: "list_repo_labels"` for a label name, `milestone_read method: "list"` for a milestone title.

View File

@@ -68,7 +68,7 @@ List responses trim PRs down to summary fields — `head`/`base` are bare ref st
Merge-specific parameters (`merge_style`, `delete_branch`, `force_merge`, `merge_when_checks_succeed`, `head_commit_id`, `message` as merge commit message) are covered in `references/merging.md`.
**`"create"` silently drops most optional parameters.** `"create"` reads only `owner`, `repo`, `title`, `body`, `head`, `base`, `draft`, `labels`, and `deadline`. Every other optional parameter — including `assignee`, `assignees`, `milestone`, `reviewers`, `team_reviewers` and `remove_deadline` — is accepted without error and discarded. There is no error, no warning, and nothing in the response distinguishing a dropped parameter from one that was never passed: the response simply omits the key. Setting any of them requires a second call after the PR exists — `"update"` for `assignee`/`assignees`/`milestone`, `"add_reviewers"` for `reviewers`/`team_reviewers`.
**`"create"` silently drops most optional parameters.** `"create"` reads only `owner`, `repo`, `title`, `body`, `head`, `base`, `draft`, `labels`, and `deadline`. Every other optional parameter — including `assignee`, `assignees`, `milestone`, `reviewers`, `team_reviewers` and `remove_deadline` — is accepted without error and discarded. Nothing marks the drop: no error, no warning, and nothing in the response distinguishing a dropped parameter from one that was never passed — the response simply omits the key. Setting any of them requires a second call after the PR exists — `"update"` for `assignee`/`assignees`/`milestone`, `"add_reviewers"` for `reviewers`/`team_reviewers`.
Two things make this easy to miss:

View File

@@ -6,9 +6,15 @@ Manage Gitea releases and tags — list, create, and delete releases (with draft
This skill handles release and tag operations for a Gitea repository. It creates releases from a tag/target commitish with title, notes, and draft/prerelease flags; lists and paginates releases and tags; retrieves the latest release; and deletes releases and tags as separate, independent destructive operations. It resolves the numeric release id required for deletion instead of assuming a tag name will work.
## Before you start
Requires a Gitea MCP server configured with a token holding `write:repository`. Requires a git remote
named `origin` pointing at the Gitea instance, unless an orchestrating caller already resolved
`owner`/`repo` for you.
## Usage
```
```text
/gitea-releases
```
@@ -19,6 +25,6 @@ Describe your release/tag task: list releases, get the latest release, create a
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/call-signatures.md` | Tool parameters and response shapes derived from gitea-mcp source (see `references/sources.md`); input params for 3 of the 9 tools additionally live-cross-checked |
| `references/call-signatures.md` | Tool parameters and response shapes derived from gitea-mcp source (see `references/sources.md`); input params for all nine tools additionally cross-checked live against gitea-mcp v1.7.0 |
| `references/conventions.md` | Semver/draft/prerelease practitioner conventions and pagination behavior |
| `references/sources.md` | Research sources backing the call signatures and conventions |

View File

@@ -6,22 +6,40 @@ description: >
create, or delete either — even when the user does not say "release" or
"Gitea". Not branches or commit history -> `gitea-branches`.
compatibility: Requires Gitea MCP server configured with a token with write:repository scope, which
gates every release and tag tool here. Requires git remote "origin" pointing to the Gitea instance
for owner/repo resolution, unless an orchestrating caller passes them already resolved.
metadata:
category: gitea
category: integration
version: "0.1.0"
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
- context7-websites-gitea
- context7-gitea-tea-cli
allowed-tools: Bash mcp__gitea__list_releases mcp__gitea__get_release mcp__gitea__get_latest_release mcp__gitea__create_release mcp__gitea__delete_release mcp__gitea__list_tags mcp__gitea__get_tag mcp__gitea__create_tag mcp__gitea__delete_tag
---
## Gotchas
- **Deleting a release never deletes its tag.** A release is a metadata wrapper around a tag, so removing both takes two independent destructive calls. The reverse — whether deleting a tag deletes its release — is *unconfirmed*; verify with `list_releases`/`get_release` after `delete_tag` rather than assume it survives.
- **Set `is_draft`/`is_pre_release` explicitly on every `create_release` — Gitea never infers a prerelease from a `-beta`/`-rc` tag name.** A tag named `v2.0.0-beta.1` publishes as a full release, and becomes the repo's latest, unless `is_pre_release: true` is passed in the same call. The response object names them `draft`/`prerelease`; passing `draft` as an input key is silently ignored, not rejected.
- **`list_releases`/`list_tags` default `per_page` to 20**, where most other gitea-mcp list tools default to 30 — a caller assuming 30 under-counts the pages a full sweep needs.
- **`delete_release` takes the numeric `id`, never a `tag_name`; `delete_tag` takes the tag name, never an id.** Holding only a tag name, resolve the release id through `list_releases` or `get_release` first — a tag name passed to `delete_release` fails, and that failure is not evidence the release is already gone.
- **Deleting a release never deletes its tag**, and the reverse direction is *unconfirmed* — verify with `list_releases`/`get_release` after `delete_tag`. Removing both takes two independent destructive calls.
- **Set `is_draft`/`is_pre_release` explicitly on every `create_release`** — Gitea infers neither from a `-beta`/`-rc` tag name, so `v2.0.0-beta.1` publishes as a full release and becomes the repo's latest. `draft`/`prerelease` are output field names only; passing `draft` as an input key is silently ignored.
- **`list_releases`/`list_tags` default `per_page` to 20**, where most other gitea-mcp list tools default to 30 — a caller assuming 30 under-counts pages.
## Dispatch table
## Step 1 — Resolve owner and repo
`owner` and `repo` are required on every tool below. Extract them from the git remote, unless an orchestrating caller passed them in already:
```bash
git remote get-url origin
```
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
## Step 2 — Dispatch
| Action | Tool | Required params | Optional params |
|---|---|---|---|
@@ -37,13 +55,17 @@ metadata:
`target` (on `create_release`/`create_tag`) is a commitish — a branch name, existing tag, or commit SHA — the point the new tag is cut from.
Pass a caller-supplied `tag_name` through verbatim. Semver with a `v` prefix is a tooling convention, not a Gitea constraint — the API accepts any string — so never validate or rewrite it.
Pass a caller-supplied `tag_name` through verbatim — the API accepts any string, and semver with a `v` prefix is a tooling convention rather than a Gitea constraint.
## Workflow
## Step 3 — Procedure for the scenario in hand
- [ ] **Creating a release:** Call `create_release` with `tag_name`, `target`, `title`, and `is_draft`/`is_pre_release` set explicitly — never left to default. **There is no update or edit tool on this surface**: `create_release`, `get_release`, `get_latest_release`, `list_releases` and `delete_release` are the whole set. A release published with the wrong flag therefore has no non-destructive repair — the only fix is `delete_release` plus a fresh `create_release`. Gitea is assumed to create the tag from `target` when `tag_name` does not yet exist — plausible from the API shape, not confirmed in the research docs — so a separate `create_tag` is only needed to tag a commit without wrapping it in a release. Verify with `get_tag` afterward if the caller depends on it.
- [ ] **Deleting a release:** `delete_release` takes the numeric `id` and never a `tag_name`; `delete_tag` is the mirror opposite and never takes an id. With only a tag name in hand, resolve the id through `list_releases` (paginating if needed) or `get_release` first.
- [ ] **Deleting a tag along with its release:** Delete the release first, then call `delete_tag` — confirm both are intended before proceeding, since each is irreversible on its own.
- [ ] **Listing every page:** Loop `page: 1, 2, 3...` until a response returns fewer than `per_page` entries. Nothing here auto-paginates.
These four are mutually exclusive — pick the one row the request lands on.
| Scenario | Procedure |
|---|---|
| Create a release | Call `create_release` with `tag_name`, `target`, `title`, and `is_draft`/`is_pre_release` set explicitly — never left to default. This surface carries no update or edit tool, so a wrong flag is repairable only by delete-and-recreate (`references/conventions.md`). A separate `create_tag` is only needed to tag a commit without wrapping it in a release. |
| Delete a release | Resolve the numeric `id` per the first Gotcha, confirm intent, then call `delete_release`. The tag survives. |
| Delete a tag along with its release | Delete the release first, then call `delete_tag` — confirm both are intended before proceeding, since each is irreversible on its own. |
| List every page | Loop `page: 1, 2, 3...` until a response returns fewer than `per_page` entries. Nothing here auto-paginates. |
If exact input params or response field shapes are needed, read `references/call-signatures.md`. If the caller raises semver tag naming, draft/prerelease semantics, release-notes sourcing, or how a release relates to its tag, read `references/conventions.md`.

View File

@@ -11,11 +11,12 @@ Signatures and response shapes are derived from gitea-mcp source (`operation/*.g
see `references/sources.md`) rather than copied from upstream API docs, which can drift from the
deployed gitea-mcp version — but this is a source-code extraction, not a live MCP tool call.
Input parameter schemas for 3 of the 9 tools here — `create_release`, `delete_tag`, and
`get_latest_release` — were additionally cross-checked live via `ToolSearch` against the deployed
`mcp__gitea__*` tools in session 2026-07-05, and confirmed to match exactly (required/optional
params and names). That check covered only input params for those 3 tools, not response shapes,
and not the other 6 tools — treat the rest of this document as source-derived, not live-verified.
Input parameter schemas for all 9 tools here were additionally cross-checked live via `ToolSearch`
against the deployed `mcp__gitea__*` tools and confirmed to match exactly — required and optional
params, names, and defaults. Last verified against gitea-mcp **v1.7.0**, as reported by
`get_gitea_mcp_server_version`. That check covers input params only: the response shapes below
remain source-derived, not live-verified, so re-verify them if a response reads differently than
documented here.
`owner` and `repo` are required strings on every tool below and are omitted from the per-tool lists
for brevity.
@@ -44,7 +45,7 @@ for brevity.
- Does not delete the underlying tag.
**Release object shape** (returned by list/get/create/latest):
```
```text
id, tag_name, target, title, body, draft, prerelease, html_url, author, created_at, published_at
```
`author` is the creator's login. `body` holds the release notes.

View File

@@ -3,7 +3,7 @@
## gitea-mcp-repo
- **URL:** https://gitea.com/gitea/gitea-mcp
- **Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags.
- **Description:** Official gitea-mcp repository; operation/*.go source files documenting the MCP tools, their parameters, and CLI flags. Originally extracted at v1.3.0; the input parameter schemas in `references/call-signatures.md` were re-verified live via `ToolSearch` against the deployed server, **last verified at v1.7.0** as reported by `get_gitea_mcp_server_version`.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/api-reference.md (Releases and Tags section); plugins/gitea/docs/research/docs/gitea/troubleshooting.md (`delete_release` numeric-id gotcha, `per_page` defaults)
**Contributing files:**

View File

@@ -8,7 +8,7 @@ This skill is the conversational front door to the Gitea suite — it replaces t
## Usage
```
```text
/gitea-workflow
```

View File

@@ -13,7 +13,7 @@ The request names a capability but not obviously which skill owns it. Find the o
| `gitea-issues` | List/read/create/update issues, comments, search across issues and PRs. Composes `gitea-labels-milestones` for label/milestone resolution. |
| `gitea-labels-milestones` | Label and milestone CRUD, label inference from conversation context, resolving names/titles to the numeric IDs writes require. Cross-cutting — used by both `gitea-issues` and `gitea-prs`. |
| `gitea-prs` | List/read/create/update/merge PRs, code reviews. Composes `gitea-labels-milestones` the same way `gitea-issues` does. |
| `gitea-branches` | Branch list/create/delete, plus commit history (list commits, get a single commit by SHA). |
| `gitea-branches` | Branch list/create/rename/delete, plus commit history (list commits, get a single commit by SHA). |
| `gitea-files` | Read/write/delete individual files, list a directory, walk the full repo tree. |
| `gitea-releases` | Release and tag CRUD — draft/prerelease flags, release notes, semver tags. |

View File

@@ -4,7 +4,7 @@ Manage Gitea repository branches and inspect commit history via the Gitea MCP se
## What it does
This skill handles branch lifecycle operations (list, create, delete) and read-only commit
This skill handles branch lifecycle operations (list, create, rename, delete) and read-only commit
history (list commits, get a single commit's full detail) against a Gitea repository. It resolves
`owner`/`repo` from the git remote, dispatches to the right MCP tool, and applies safety and
pagination conventions specific to Gitea's API (e.g. refusing to delete a protected branch without
@@ -18,25 +18,26 @@ Branch references that only exist relative to a pull request — a PR's head or
cross-repo fork PR heads in particular — belong to `gitea-prs`; `list_branches` cannot see a fork's
head at all.
The skill triggers on phrasings like "list branches", "create a branch", "delete a branch",
The skill triggers on phrasings like "list branches", "create a branch", "rename a branch", "delete a branch",
"what commits are on this branch", "show commit <sha>", and "what changed in that commit", even
when the user does not say "Gitea", as long as the repo's remote is a Gitea instance.
## Before you start
Requires a Gitea MCP server configured with a token that has `write:repository` scope. This is
confirmed for `list_branches`, `create_branch`, and `delete_branch` (Gitea gates reads behind write
confirmed for `list_branches`, `create_branch`, and `delete_branch` (and inferred for
`rename_branch`) (Gitea gates reads behind write
scope for repo-scoped operations); `list_commits` and `get_commit` are inferred to need the same
scope by analogy, not explicitly confirmed — see `references/commits.md`. Requires a git remote
named `origin` pointing at the Gitea instance.
## Usage
```
```text
/gitea-branches
```
Describe your task: list/create/delete a branch, or list/inspect commits. See `SKILL.md`'s
Describe your task: list/create/rename/delete a branch, or list/inspect commits. See `SKILL.md`'s
dispatch table for the full set of recognized invocations.
## Files
@@ -44,6 +45,6 @@ dispatch table for the full set of recognized invocations.
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents — dispatch table, gotchas |
| `references/branches.md` | Verified call signatures and mechanics for list/create/delete branch |
| `references/branches.md` | Verified call signatures and mechanics for list/create/rename/delete branch |
| `references/commits.md` | Verified call signatures and mechanics for list/get commit |
| `references/sources.md` | Research sources backing the branch/commit guidance |

View File

@@ -2,10 +2,10 @@
name: gitea-branches
description: >
Use when listing, creating, or deleting branches in a Gitea repository, or
reading its commit history — even when the user does not say "Gitea". Not a
local working copy's branches -> `git-branches`. Not local history ->
`git-history`. Not a PR's head or base branch -> `gitea-prs`.
Use when listing, creating, renaming, or deleting branches in a Gitea repository,
or reading its commit history — even when the user does not say "Gitea". Not a
local checkout's branches -> `git-branches`. Not local history ->
`git-history`. Not a PR's head or base -> `gitea-prs`.
compatibility: Requires Gitea MCP server configured with a token with write:repository scope; this is confirmed to gate list_branches, create_branch, and delete_branch (Gitea gates reads behind write scope for repo-scoped operations), and is inferred by analogy (not explicitly confirmed by source docs) to also gate list_commits and get_commit. Requires git remote "origin" pointing to the Gitea instance.
@@ -17,7 +17,7 @@ metadata:
- gitea-mcp-slim-go
- context7-websites-gitea
allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit
allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__gitea__rename_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit
---
## Gotchas
@@ -42,17 +42,18 @@ git remote get-url origin
|---|---|
| `/gitea-branches` or `/gitea-branches list` | List branches |
| `/gitea-branches create <name> [from <base>]` | Create branch |
| `/gitea-branches rename <name> to <new-name>` | Rename branch |
| `/gitea-branches delete <name>` | Delete branch |
| `/gitea-branches commits [on <branch>] [touching <path>]` | List commit history |
| `/gitea-branches commit <sha>` | Get full detail for one commit |
For branch operations (list/create/delete), read `references/branches.md` — it carries the call signatures, the `old_branch` source rule, and the protected-branch refusal in full.
For branch operations (list/create/rename/delete), read `references/branches.md` — it carries the call signatures, the `old_branch` source rule, and the protected-branch refusal in full.
For commit operations (list/get), read `references/commits.md`.
## Step 3 — Report
For reads: display branches as name + protected flag; display commits as SHA (short), message summary, author, date.
For writes (create/delete): confirm the action taken, the branch name, and (for create) the base it forked from.
For writes (create/rename/delete): confirm the action taken, the branch name, and (for create) the base it forked from or (for rename) the name it had before.
For errors: surface the HTTP code and message, applying the 404 gotcha above before reporting "not found" to the user.

View File

@@ -7,10 +7,11 @@ source_keys:
# Branch operations
Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`
at authoring time, not copied from research docs — this is deliberate: research docs are generated
from source code at a point in time and can drift from the server actually deployed. Re-verify
against the live schema if these tools appear to behave differently than documented here.
Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`,
not copied from research docs — this is deliberate: research docs are generated from source code 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`. Re-verify against the live schema if the
deployed version differs or these tools behave differently than documented here.
## `list_branches`
@@ -21,7 +22,7 @@ against the live schema if these tools appear to behave differently than documen
- `per_page` (number, optional, default: `30`)
**Call:**
```
```text
list_branches owner: <owner> repo: <repo>
```
@@ -41,7 +42,7 @@ count is less than `per_page`.
branch server-side (not necessarily your current local checkout)
**Call:**
```
```text
create_branch owner: <owner> repo: <repo> branch: <new-name> old_branch: <source-branch>
```
@@ -53,6 +54,29 @@ top-level request with no working branch context), omit `old_branch` and let it
A branch name collision returns `409 Conflict`.
## `rename_branch`
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `branch` (string, required) — the branch's current name
- `new_name` (string, required) — the name to move it to
**Call:**
```text
rename_branch owner: <owner> repo: <repo> branch: <current-name> new_name: <new-name>
```
A rename moves the ref server-side; it is not a delete-plus-create, and no commit history is
rewritten. What it does to things *pointing at* the old name — open pull requests using it as head or
base, a branch protection rule matching it, CI config, and tracking branches on every other clone —
is **not confirmed** by this skill's sources: the deployed tool describes itself only as "Rename an
existing branch in a repository". Treat a rename of a branch with open PRs or a protection rule as a
change needing verification afterward (`list_branches`, plus `gitea-prs` for the PR side), and
confirm with the user first, exactly as for `delete_branch` below. A collision with an existing
branch name is expected to return `409 Conflict` by analogy with `create_branch`, not separately
confirmed.
## `delete_branch`
**Parameters:**
@@ -61,7 +85,7 @@ A branch name collision returns `409 Conflict`.
- `branch` (string, required)
**Call:**
```
```text
delete_branch owner: <owner> repo: <repo> branch: <name>
```
@@ -73,9 +97,12 @@ protected, every time, regardless of how the request is phrased.
## Token scope
All three — `list_branches`, `create_branch`, `delete_branch` — require `write:repository`. Gitea
`list_branches`, `create_branch` and `delete_branch` all require `write:repository`. Gitea
gates reads behind write scope for repo-scoped operations, so `list_branches` needs the same scope
as the write operations, not `write:issue` alone. An earlier version of this doc claimed
`write:issue` alone was sufficient for `list_branches`, based on empirical testing under a token
that held both `write:issue` and `write:repository` simultaneously — that test didn't isolate the
variable, so it couldn't actually establish `write:issue` alone as sufficient.
`rename_branch` is a write on the same repo-scoped surface and is inferred to need `write:repository`
too — inferred by analogy, not separately confirmed.

View File

@@ -8,8 +8,9 @@ source_keys:
# Commit operations
Read-only commit history, scoped to a repo (optionally to one branch or one path). Call signatures
below were verified live against the deployed `gitea-mcp` server via `ToolSearch` at authoring time,
not copied from research docs, for the same drift-avoidance reason noted in `references/branches.md`.
below were verified live against the deployed `gitea-mcp` server via `ToolSearch`, not copied from
research docs, for the same drift-avoidance reason noted in `references/branches.md`. **Last verified
against gitea-mcp v1.7.0**, as reported by `get_gitea_mcp_server_version`.
This domain has no prior skill precedent — it's new coverage added alongside branches because commit
history is naturally scoped to a branch (a "what happened on this branch" question), not because it
@@ -27,7 +28,7 @@ shares any tool family with branch create/delete.
- `per_page` (number, optional, default: `30`, minimum: `1`)
**Call:**
```
```text
list_commits owner: <owner> repo: <repo> sha: <branch-or-sha> path: <optional-path>
```
@@ -52,7 +53,7 @@ Paginate per the pagination Gotcha in SKILL.md if you need more than one page of
- `sha` (string, required)
**Call:**
```
```text
get_commit owner: <owner> repo: <repo> sha: <commit-sha>
```

View File

@@ -2,8 +2,8 @@
**Note on call signatures:** per `docs/adr/0011-gitea-skill-deep-modules.md`, the tool parameter
signatures in `references/branches.md` and `references/commits.md` were re-verified live via
`ToolSearch` against the deployed `gitea-mcp` server at authoring time — they are not copied
verbatim from `api-reference.md` below. This resolves issue #6 comment #849's root-cause finding
`ToolSearch` against the deployed `gitea-mcp` server — **last verified against v1.7.0**, as reported
by `get_gitea_mcp_server_version` — rather than copied verbatim from `api-reference.md` below. This resolves issue #6 comment #849's root-cause finding
that a prior skill was authored from API docs that had drifted from the actual MCP tool schema.
The research docs cited here informed gotchas, response shapes, and workflow context, not the
parameter lists themselves.
@@ -11,7 +11,7 @@ parameter lists themselves.
## gitea-mcp-repo
- **URL:** https://gitea.com/gitea/gitea-mcp
- **Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags. Informed the dispatch table and pagination / 404-may-mean-403 gotchas in SKILL.md, and the list/create/delete branch and list/get commit mechanics (including 409 conflict and default-branch fallback behavior) in references/branches.md and references/commits.md.
- **Description:** Official gitea-mcp repository; operation/*.go source files documenting the MCP tools, their parameters, and CLI flags. Extracted at v1.3.0; the parameter lists carried into this skill are re-verified live against the deployed server, last at v1.7.0. Informed the dispatch table and pagination / 404-may-mean-403 gotchas in SKILL.md, and the list/create/delete branch and list/get commit mechanics (including 409 conflict and default-branch fallback behavior) in references/branches.md and references/commits.md.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md, references/branches.md, references/commits.md
- **Status:** `extracted`
@@ -19,7 +19,7 @@ parameter lists themselves.
## gitea-mcp-slim-go
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/repo/slim.go
- **Description:** Slim response shape structs from gitea-mcp source; defines exactly which fields the MCP server returns for branches (name, protected, commit_sha) and commits (sha, html_url, created, message, author), and informed get_commit's always-populated guarantee vs. list_commits' conditional fields.
- **Description:** Slim response shape structs from gitea-mcp source, extracted at v1.3.0; defines exactly which fields the MCP server returns for branches (name, protected, commit_sha) and commits (sha, html_url, created, message, author), and informed get_commit's always-populated guarantee vs. list_commits' conditional fields.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** references/branches.md, references/commits.md
- **Status:** `extracted`

View File

@@ -8,7 +8,7 @@ This skill handles file-domain operations within the Gitea integration suite: re
## Usage
```
```text
/gitea-files
```

View File

@@ -24,7 +24,7 @@ allowed-tools: mcp__gitea__get_file_contents mcp__gitea__get_dir_contents mcp__g
- **A 404 may mean an under-scoped token, not a missing path.** Every tool here gates on `write:repository`, and Gitea masks insufficient scope as 404. Check scopes first.
- **Reads take `ref`, writes take `branch_name`.** One concept, two parameter names — chaining a read into a write drops the branch if you carry the wrong key.
- **`content` is base64 both ways.** Encode before a write, decode after a read; `withLines: true` returns numbered lines.
- **`content` is base64 both ways.** Encode before a write, decode after a read.
## Inputs
@@ -32,14 +32,12 @@ allowed-tools: mcp__gitea__get_file_contents mcp__gitea__get_dir_contents mcp__g
## Dispatch
Read the reference for the row you land on before making the call.
| Condition | Flow | Reference |
|---|---|---|
| Read one file, list one directory level, or walk the repository tree | Read | `references/reading.md` |
| Create, update, or delete a file | Write | `references/writing.md` |
If the request only inspects repository contents, read `references/reading.md` — it carries the three read tools, their pagination behaviour, and why neither a directory listing nor a tree entry supplies the SHA a write needs.
If the request creates, updates or deletes a file, read `references/writing.md` — it carries the SHA-first sequence every update and delete depends on, the worked multi-call sequence, and how to triage a write that fails.
| Read one file, list one directory level, or walk the repository tree | Read | `references/reading.md` — the three read tools, their pagination behaviour, and why neither a directory listing nor a tree entry supplies the SHA a write needs |
| Create, update, or delete a file | Write | `references/writing.md` — the SHA-first sequence every update and delete depends on, the worked multi-call sequence, and how to triage a write that fails |
A request that reads and then writes runs both flows in that order: fetch the file first, then write with the SHA that call returned.

View File

@@ -29,15 +29,17 @@ commit lands on it in one call, replacing a separate branch-creation step.
## Delete a file
Same SHA-first pattern, with no create-style fallback — `delete_file` without `sha` returns
HTTP 422.
Same SHA-first pattern, with no create-style fallback. `sha` is schema-**required** on
`delete_file`, unlike `create_or_update_file` where omitting it means *create* — so an omitted `sha`
is rejected client-side by input validation and the call never reaches Gitea. The HTTP 422 that is
actually reachable here is the stale-`sha` case.
1. `get_file_contents(owner, repo, ref: <branch>, path)` → read the top-level `sha`.
2. `delete_file(owner, repo, path, message, branch_name, sha: <that value>)`.
## Worked sequence — new file on a new branch, then a PR
```
```text
1. create_or_update_file
owner, repo
path: "docs/example.md"
@@ -58,9 +60,10 @@ when the write replaces an existing one.
| Symptom | Cause | Action |
|---|---|---|
| HTTP 409 | `sha` omitted on a path that already exists | Fetch the current SHA, retry as an update |
| HTTP 422 | `sha` missing or stale | Re-fetch the SHA immediately before the write |
| HTTP 422 | Stale `sha` — the file changed between the read and the write | Re-fetch the SHA immediately before the write |
| 403 or 422 with no SHA explanation | Branch protection requires signed commits | Stop and report |
| HTTP 413 | Reverse-proxy body limit in front of Gitea | Report; retrying cannot fix it |
| Client-side input-validation error naming `sha` | `sha` omitted on `delete_file`, where it is schema-required | Fetch the current SHA and retry — nothing was sent to Gitea |
| HTTP 404 | Wrong path, or a token without `write:repository` | Verify the path, then the token's scopes |
**Signed commits.** These writes create commits server-side from a bare API token with no 2FA or

View File

@@ -10,9 +10,8 @@ its state, adding/editing comments, applying labels, and searching issues/PRs ac
The create flow closes out four enrichments deferred from issue #6 comment #848: label inference
and milestone assignment (both by composing `gitea-labels-milestones`), an assignee workaround for
the blocked `get_me` scope, and the "Depends on #N" dependency-linking convention. It supersedes the
`issue`/`issue <N>`/`issue close <N>`/`issue comment <N>` dispatch in the old flat
`plugins/bin/skills/gitea/SKILL.md`, removed per
`docs/adr/0011-gitea-skill-deep-modules.md`.
`issue`/`issue <N>`/`issue close <N>`/`issue comment <N>` dispatch this plugin's old single flat
Gitea skill carried, retired when the plugin was split into per-domain deep modules.
## Before you start
@@ -34,7 +33,7 @@ issue request: local git branch or commit work belongs to `gitea-branches` (Gite
## Usage
```
```text
/gitea-issues
```

View File

@@ -84,8 +84,8 @@ repo-scoped number space. Use the bare `#N` form for same-repo dependencies; use
a dependency in a different repo.
When creating an issue that depends on another, append a line like:
```
```text
Depends on #42
```
to the body before calling `issue_write method: "create"`. There is no separate field or follow-up
call — the rendering happens automatically once the body is saved.
to the body before calling `issue_write method: "create"`. No separate field or follow-up call is
involved — the rendering happens automatically once the body is saved.

View File

@@ -37,7 +37,7 @@ item is the `html_url` path segment (`/issues/` vs `/pulls/`), since `is_pull` i
list items — see the Gotchas section of SKILL.md.
**Call:**
```
```text
list_issues owner: <owner> repo: <repo> state: "open" type: "issues"
```
@@ -66,7 +66,7 @@ number is backed by a pull request — absent, not `false`, on true issues).
to name strings, unlike the labels array on `get`).
**Call:**
```
```text
issue_read method: "get" owner: <owner> repo: <repo> issue_number: <N>
```
@@ -95,7 +95,7 @@ gotcha in SKILL.md.
- `remove_deadline` (boolean, optional)
**Create:**
```
```text
issue_write method: "create"
owner: <owner> repo: <repo>
title: <title> body: <body>
@@ -105,19 +105,19 @@ issue_write method: "create"
```
**Close:**
```
```text
issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed"
```
There is no `method: "close"` — using one will error.
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"`.

View File

@@ -22,12 +22,12 @@ time (see `references/sources.md`) — confirmed to match `api-reference.md`.
- `per_page` (number, optional, default `30`)
**Call:**
```
```text
search_issues query: <text>
```
**Narrowing the search:**
```
```text
search_issues query: <text> owner: <owner> state: "open" type: "pulls" labels: "bug,urgent"
```

View File

@@ -14,7 +14,7 @@ That relationship is documented here rather than in the skill description, which
## Usage
```
```text
/gitea-labels-milestones
```

View File

@@ -24,7 +24,7 @@ allowed-tools: Bash mcp__gitea__label_read mcp__gitea__label_write mcp__gitea__m
- **Applying a label takes a numeric ID, but issue/PR responses slim labels down to name strings.** An issue's existing labels yield no IDs — resolve name → ID with `label_read`.
- **`pull_request_read` returns `milestone` as a bare title string** where `issue_read` returns `{id, title}` — recover the milestone's ID by listing milestones and matching the title.
- **Never assume a `Kind/*`/`Priority/*`/`Status/*` scope is exclusive — read each label's own `exclusive` field.** `list_repo_labels` returns it per repo label, it is not org-only, and where it is `true` Gitea enforces one-per-scope itself. Replacing rather than stacking on a label whose `exclusive` is `false` destroys a valid label.
- **Never assume a `Kind/*`/`Priority/*`/`Status/*` scope is exclusive — read each label's own `exclusive` field.** `list_repo_labels` returns it on every repo label, so it is always *readable* per label; `label_write` documents it as "(org only)" because it is only *settable* through the org create methods. Where it is `true` Gitea enforces one-per-scope itself, and replacing rather than stacking on a label whose `exclusive` is `false` destroys a valid label.
## Step 1 — Resolve owner, repo and org

View File

@@ -12,15 +12,11 @@ description) to this repo's `Kind/*` / `Priority/*` / `Status/*` label taxonomy.
`gitea-issues` and `gitea-prs` before creating or updating an issue/PR, and directly when the user
asks to label something without naming exact labels.
## Exclusivity is per label — read it, never infer it
## Branching on exclusivity
Gitea's `exclusive` flag is a real per-label boolean returned by `list_repo_labels`, and where it is
`true` the server enforces one-label-per-scope itself. It is not an org-only setting, and the `/`
delimiter in a name says nothing about it. Verified on `Defame1297/holocron`: every `Priority/*`,
`Reviewed/*` and `Status/*` label is `exclusive: true`, while every `Kind/*` label — and
`Compat/Breaking` — is `exclusive: false` and is used stacked.
So read each candidate label's own `exclusive` value from the resolution call and branch on it:
`references/labels.md` owns the exclusivity rule and the read-versus-write asymmetry behind it. Read
it there rather than assuming a scope's behaviour from its name. Inference needs only the branch:
carry each candidate label's own `exclusive` value forward from the resolution call and act on it.
- **`exclusive: true`** — the server drops the sibling on write. Add the label and let it; do not
pre-remove the label already there, and do not compute a replacement set client-side. Inferring
@@ -29,7 +25,7 @@ So read each candidate label's own `exclusive` value from the resolution call an
same scope destroys a valid one: an issue can legitimately carry `Kind/Bug` and `Kind/Security`
at once.
There is no client-side exclusivity convention for this skill to enforce.
This skill enforces no client-side exclusivity convention of its own.
## Signal → label mapping
@@ -42,6 +38,7 @@ There is no client-side exclusivity convention for this skill to enforce.
| Improvement to existing behavior, "make X better", refactor with behavior change | `Kind/Enhancement` |
| Docs-only change, README/comment/guide updates | `Kind/Documentation` |
| Vulnerability, credential exposure, injection risk, auth bypass | `Kind/Security` |
| Test coverage, "add tests for X", a missing or flaky test, a test-only change | `Kind/Testing` |
**`Priority/*`** (urgency):
@@ -49,6 +46,7 @@ There is no client-side exclusivity convention for this skill to enforce.
|---|---|
| "blocking", "critical", "urgent", production-down | `Priority/Critical` |
| "soon", "high priority", "should do this sprint" | `Priority/High` |
| "low priority", "nice to have", "whenever", explicitly deferred | `Priority/Low` |
| No urgency signal present | `Priority/Medium` (default) |
**`Status/*`** (workflow state):
@@ -57,6 +55,10 @@ There is no client-side exclusivity convention for this skill to enforce.
|---|---|
| Explicit statement that the work is blocked on something else | `Status/Blocked` |
The label names in all three tables are the taxonomy this guide was written against; none of them is
guaranteed to exist on the target repo. Step 2 below resolves every inferred name against the live
label set, and a name that does not resolve is reported rather than substituted.
## Procedure
1. Read the conversation context (issue/PR title, body, or the triggering discussion) for the

View File

@@ -46,30 +46,37 @@ 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; the field is not org-only. Verified on
`Defame1297/holocron`: all `Priority/*`, `Reviewed/*` and `Status/*` labels are `exclusive: true`,
while all `Kind/*` labels and `Compat/Breaking` are `exclusive: false`. Where it 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.
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
There is no direct name lookup. 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
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
@@ -82,7 +89,7 @@ Resolution is the required first step before any label application on an issue o
## Create a label
```
```text
label_write method: "create_repo_label"
owner: <owner> repo: <repo>
name: "Kind/Bug"
@@ -98,7 +105,7 @@ tool — it is set in the Gitea UI or against the REST API directly, and read ba
## Edit a label
```
```text
label_write method: "edit_repo_label" owner: <owner> repo: <repo> id: <id> color: "#ff0000"
```
@@ -106,7 +113,7 @@ Only pass the fields being changed — `id` plus any of `name`/`color`/`descript
## Delete a label
```
```text
label_write method: "delete_repo_label" owner: <owner> repo: <repo> id: <id>
```

View File

@@ -43,7 +43,7 @@ schema level — there's no scope variant to omit them for.
## List milestones
```
```text
milestone_read method: "list" owner: <owner> repo: <repo> state: "open"
```
@@ -51,7 +51,7 @@ Report each as: id, title, state, due date, open/closed issue counts.
## Get one milestone
```
```text
milestone_read method: "get" owner: <owner> repo: <repo> id: <id>
```
@@ -60,7 +60,7 @@ milestone_read method: "get" owner: <owner> repo: <repo> id: <id>
Needed whenever the only handle available is a title — e.g. a `pull_request_read` response, which
returns `milestone` as a bare title string rather than `{id, title}`. Call:
```
```text
milestone_read method: "list" owner: <owner> repo: <repo> name: <title>
```
@@ -69,7 +69,7 @@ title typo or case mismatch), fall back to listing without the filter and matchi
## Create a milestone
```
```text
milestone_write method: "create"
owner: <owner> repo: <repo>
title: "v1.0"
@@ -82,7 +82,7 @@ this milestone via `issue_write`/`pull_request_write`.
## Update or close a milestone
```
```text
milestone_write method: "update" owner: <owner> repo: <repo> id: <id> state: "closed"
```
@@ -90,7 +90,7 @@ Only pass the fields being changed — `id` plus any of `title`/`description`/`d
## Delete a milestone
```
```text
milestone_write method: "delete" owner: <owner> repo: <repo> id: <id>
```

View File

@@ -8,11 +8,17 @@ This skill handles the pull request lifecycle within the Gitea integration suite
## Usage
```
```text
/gitea-prs
```
Describe the PR or review task: list PRs, get a PR's status/diff/reviews, create or update a PR, merge one, or create/submit/dismiss a code review. The skill will determine owner/repo from context and resolve any label or milestone names via `gitea-labels-milestones` before writing them.
Describe the PR or review task: list PRs, get a PR's status/diff/reviews, create or update a PR, merge one, or create/submit/dismiss a code review. The skill resolves `owner`/`repo` from the `origin` git remote (or takes them from an orchestrating caller) and resolves any label or milestone names via `gitea-labels-milestones` before writing them.
## Before you start
Requires a Gitea MCP server configured with a token holding `write:issue` + `write:repository`.
Requires a git remote named `origin` pointing at the Gitea instance, unless an orchestrating caller
(e.g. `gitea-workflow`) already resolved `owner`/`repo` for you.
## Files

View File

@@ -5,7 +5,10 @@ description: >
Use when listing, reading, creating, updating, merging, or reviewing Gitea pull requests — even
when the user does not say "Gitea". Not issues -> `gitea-issues`.
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token scopes.
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token
scopes. Requires git remote "origin" pointing to the Gitea instance for owner/repo resolution when
invoked directly by a human; an orchestrating caller (e.g. gitea-workflow) may pass owner/repo
already resolved.
metadata:
category: integration
@@ -16,7 +19,7 @@ metadata:
- context7-gitea-tea-cli
version: "0.1.2"
allowed-tools: mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp__gitea__pull_request_write mcp__gitea__pull_request_review_write
allowed-tools: Bash mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp__gitea__pull_request_write mcp__gitea__pull_request_review_write
---
## Gotchas
@@ -24,9 +27,19 @@ allowed-tools: mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp_
- **Issues and PRs share one number space.** `#42` may be an issue rather than a PR. When unsure, call `pull_request_read method: "get"` and read a 404 as "that number is an issue" — hand it to `gitea-issues`.
- **`pull_request_write method: "create"` discards most optional parameters in silence.** `milestone`, `assignee`, `assignees`, `reviewers` and `team_reviewers` are accepted, dropped, and left out of the response, so a drop is indistinguishable from never passing them. `labels` *does* apply on `"create"`, so labels landing is no evidence the milestone did.
## Dispatch
## Step 1 — Resolve owner and repo
Resolve `owner` and `repo` from context first, and confirm the number names a PR before writing to it.
Extract them from the git remote before any tool call, skipping this when an orchestrating caller already passed them in:
```bash
git remote get-url origin
```
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
## Step 2 — Dispatch
Confirm the number names a PR, not an issue, before writing to it.
| Task | Tool | Reference |
|---|---|---|
@@ -36,11 +49,9 @@ Resolve `owner` and `repo` from context first, and confirm the number names a PR
| Merge a PR, or judge whether it can merge | `pull_request_write method: "merge"` | `references/merging.md` |
| Read, create, submit, dismiss or delete a code review, or reply to and resolve a review comment thread | `pull_request_read`, `pull_request_review_write` | `references/reviews.md` |
Whatever `"create"` dropped takes a second call once the PR exists — `"update"` for milestone and assignees, `"add_reviewers"` for reviewers.
Read the reference for the row you land on before making the call. Each carries the parameter signatures, the per-method behaviour and the response-shape quirks the row cannot, and every write method has at least one parameter that behaves differently from its issue-side counterpart.
## Resolving labels and milestones
## Step 3 — Resolving labels and milestones
`labels` and `milestone` take numeric IDs, never name or title strings. Before a `pull_request_write` call carrying either, resolve them through `gitea-labels-milestones`: `label_read method: "list_repo_labels"` for a label name, `milestone_read method: "list"` for a milestone title.

View File

@@ -68,7 +68,7 @@ List responses trim PRs down to summary fields — `head`/`base` are bare ref st
Merge-specific parameters (`merge_style`, `delete_branch`, `force_merge`, `merge_when_checks_succeed`, `head_commit_id`, `message` as merge commit message) are covered in `references/merging.md`.
**`"create"` silently drops most optional parameters.** `"create"` reads only `owner`, `repo`, `title`, `body`, `head`, `base`, `draft`, `labels`, and `deadline`. Every other optional parameter — including `assignee`, `assignees`, `milestone`, `reviewers`, `team_reviewers` and `remove_deadline` — is accepted without error and discarded. There is no error, no warning, and nothing in the response distinguishing a dropped parameter from one that was never passed: the response simply omits the key. Setting any of them requires a second call after the PR exists — `"update"` for `assignee`/`assignees`/`milestone`, `"add_reviewers"` for `reviewers`/`team_reviewers`.
**`"create"` silently drops most optional parameters.** `"create"` reads only `owner`, `repo`, `title`, `body`, `head`, `base`, `draft`, `labels`, and `deadline`. Every other optional parameter — including `assignee`, `assignees`, `milestone`, `reviewers`, `team_reviewers` and `remove_deadline` — is accepted without error and discarded. Nothing marks the drop: no error, no warning, and nothing in the response distinguishing a dropped parameter from one that was never passed — the response simply omits the key. Setting any of them requires a second call after the PR exists — `"update"` for `assignee`/`assignees`/`milestone`, `"add_reviewers"` for `reviewers`/`team_reviewers`.
Two things make this easy to miss:

View File

@@ -6,9 +6,15 @@ Manage Gitea releases and tags — list, create, and delete releases (with draft
This skill handles release and tag operations for a Gitea repository. It creates releases from a tag/target commitish with title, notes, and draft/prerelease flags; lists and paginates releases and tags; retrieves the latest release; and deletes releases and tags as separate, independent destructive operations. It resolves the numeric release id required for deletion instead of assuming a tag name will work.
## Before you start
Requires a Gitea MCP server configured with a token holding `write:repository`. Requires a git remote
named `origin` pointing at the Gitea instance, unless an orchestrating caller already resolved
`owner`/`repo` for you.
## Usage
```
```text
/gitea-releases
```
@@ -19,6 +25,6 @@ Describe your release/tag task: list releases, get the latest release, create a
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/call-signatures.md` | Tool parameters and response shapes derived from gitea-mcp source (see `references/sources.md`); input params for 3 of the 9 tools additionally live-cross-checked |
| `references/call-signatures.md` | Tool parameters and response shapes derived from gitea-mcp source (see `references/sources.md`); input params for all nine tools additionally cross-checked live against gitea-mcp v1.7.0 |
| `references/conventions.md` | Semver/draft/prerelease practitioner conventions and pagination behavior |
| `references/sources.md` | Research sources backing the call signatures and conventions |

View File

@@ -6,22 +6,40 @@ description: >
create, or delete either — even when the user does not say "release" or
"Gitea". Not branches or commit history -> `gitea-branches`.
compatibility: Requires Gitea MCP server configured with a token with write:repository scope, which
gates every release and tag tool here. Requires git remote "origin" pointing to the Gitea instance
for owner/repo resolution, unless an orchestrating caller passes them already resolved.
metadata:
category: gitea
category: integration
version: "0.1.0"
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
- context7-websites-gitea
- context7-gitea-tea-cli
allowed-tools: Bash mcp__gitea__list_releases mcp__gitea__get_release mcp__gitea__get_latest_release mcp__gitea__create_release mcp__gitea__delete_release mcp__gitea__list_tags mcp__gitea__get_tag mcp__gitea__create_tag mcp__gitea__delete_tag
---
## Gotchas
- **Deleting a release never deletes its tag.** A release is a metadata wrapper around a tag, so removing both takes two independent destructive calls. The reverse — whether deleting a tag deletes its release — is *unconfirmed*; verify with `list_releases`/`get_release` after `delete_tag` rather than assume it survives.
- **Set `is_draft`/`is_pre_release` explicitly on every `create_release` — Gitea never infers a prerelease from a `-beta`/`-rc` tag name.** A tag named `v2.0.0-beta.1` publishes as a full release, and becomes the repo's latest, unless `is_pre_release: true` is passed in the same call. The response object names them `draft`/`prerelease`; passing `draft` as an input key is silently ignored, not rejected.
- **`list_releases`/`list_tags` default `per_page` to 20**, where most other gitea-mcp list tools default to 30 — a caller assuming 30 under-counts the pages a full sweep needs.
- **`delete_release` takes the numeric `id`, never a `tag_name`; `delete_tag` takes the tag name, never an id.** Holding only a tag name, resolve the release id through `list_releases` or `get_release` first — a tag name passed to `delete_release` fails, and that failure is not evidence the release is already gone.
- **Deleting a release never deletes its tag**, and the reverse direction is *unconfirmed* — verify with `list_releases`/`get_release` after `delete_tag`. Removing both takes two independent destructive calls.
- **Set `is_draft`/`is_pre_release` explicitly on every `create_release`** — Gitea infers neither from a `-beta`/`-rc` tag name, so `v2.0.0-beta.1` publishes as a full release and becomes the repo's latest. `draft`/`prerelease` are output field names only; passing `draft` as an input key is silently ignored.
- **`list_releases`/`list_tags` default `per_page` to 20**, where most other gitea-mcp list tools default to 30 — a caller assuming 30 under-counts pages.
## Dispatch table
## Step 1 — Resolve owner and repo
`owner` and `repo` are required on every tool below. Extract them from the git remote, unless an orchestrating caller passed them in already:
```bash
git remote get-url origin
```
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
## Step 2 — Dispatch
| Action | Tool | Required params | Optional params |
|---|---|---|---|
@@ -37,13 +55,17 @@ metadata:
`target` (on `create_release`/`create_tag`) is a commitish — a branch name, existing tag, or commit SHA — the point the new tag is cut from.
Pass a caller-supplied `tag_name` through verbatim. Semver with a `v` prefix is a tooling convention, not a Gitea constraint — the API accepts any string — so never validate or rewrite it.
Pass a caller-supplied `tag_name` through verbatim — the API accepts any string, and semver with a `v` prefix is a tooling convention rather than a Gitea constraint.
## Workflow
## Step 3 — Procedure for the scenario in hand
- [ ] **Creating a release:** Call `create_release` with `tag_name`, `target`, `title`, and `is_draft`/`is_pre_release` set explicitly — never left to default. **There is no update or edit tool on this surface**: `create_release`, `get_release`, `get_latest_release`, `list_releases` and `delete_release` are the whole set. A release published with the wrong flag therefore has no non-destructive repair — the only fix is `delete_release` plus a fresh `create_release`. Gitea is assumed to create the tag from `target` when `tag_name` does not yet exist — plausible from the API shape, not confirmed in the research docs — so a separate `create_tag` is only needed to tag a commit without wrapping it in a release. Verify with `get_tag` afterward if the caller depends on it.
- [ ] **Deleting a release:** `delete_release` takes the numeric `id` and never a `tag_name`; `delete_tag` is the mirror opposite and never takes an id. With only a tag name in hand, resolve the id through `list_releases` (paginating if needed) or `get_release` first.
- [ ] **Deleting a tag along with its release:** Delete the release first, then call `delete_tag` — confirm both are intended before proceeding, since each is irreversible on its own.
- [ ] **Listing every page:** Loop `page: 1, 2, 3...` until a response returns fewer than `per_page` entries. Nothing here auto-paginates.
These four are mutually exclusive — pick the one row the request lands on.
| Scenario | Procedure |
|---|---|
| Create a release | Call `create_release` with `tag_name`, `target`, `title`, and `is_draft`/`is_pre_release` set explicitly — never left to default. This surface carries no update or edit tool, so a wrong flag is repairable only by delete-and-recreate (`references/conventions.md`). A separate `create_tag` is only needed to tag a commit without wrapping it in a release. |
| Delete a release | Resolve the numeric `id` per the first Gotcha, confirm intent, then call `delete_release`. The tag survives. |
| Delete a tag along with its release | Delete the release first, then call `delete_tag` — confirm both are intended before proceeding, since each is irreversible on its own. |
| List every page | Loop `page: 1, 2, 3...` until a response returns fewer than `per_page` entries. Nothing here auto-paginates. |
If exact input params or response field shapes are needed, read `references/call-signatures.md`. If the caller raises semver tag naming, draft/prerelease semantics, release-notes sourcing, or how a release relates to its tag, read `references/conventions.md`.

View File

@@ -11,11 +11,12 @@ Signatures and response shapes are derived from gitea-mcp source (`operation/*.g
see `references/sources.md`) rather than copied from upstream API docs, which can drift from the
deployed gitea-mcp version — but this is a source-code extraction, not a live MCP tool call.
Input parameter schemas for 3 of the 9 tools here — `create_release`, `delete_tag`, and
`get_latest_release` — were additionally cross-checked live via `ToolSearch` against the deployed
`mcp__gitea__*` tools in session 2026-07-05, and confirmed to match exactly (required/optional
params and names). That check covered only input params for those 3 tools, not response shapes,
and not the other 6 tools — treat the rest of this document as source-derived, not live-verified.
Input parameter schemas for all 9 tools here were additionally cross-checked live via `ToolSearch`
against the deployed `mcp__gitea__*` tools and confirmed to match exactly — required and optional
params, names, and defaults. Last verified against gitea-mcp **v1.7.0**, as reported by
`get_gitea_mcp_server_version`. That check covers input params only: the response shapes below
remain source-derived, not live-verified, so re-verify them if a response reads differently than
documented here.
`owner` and `repo` are required strings on every tool below and are omitted from the per-tool lists
for brevity.
@@ -44,7 +45,7 @@ for brevity.
- Does not delete the underlying tag.
**Release object shape** (returned by list/get/create/latest):
```
```text
id, tag_name, target, title, body, draft, prerelease, html_url, author, created_at, published_at
```
`author` is the creator's login. `body` holds the release notes.

View File

@@ -3,7 +3,7 @@
## gitea-mcp-repo
- **URL:** https://gitea.com/gitea/gitea-mcp
- **Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags.
- **Description:** Official gitea-mcp repository; operation/*.go source files documenting the MCP tools, their parameters, and CLI flags. Originally extracted at v1.3.0; the input parameter schemas in `references/call-signatures.md` were re-verified live via `ToolSearch` against the deployed server, **last verified at v1.7.0** as reported by `get_gitea_mcp_server_version`.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/api-reference.md (Releases and Tags section); plugins/gitea/docs/research/docs/gitea/troubleshooting.md (`delete_release` numeric-id gotcha, `per_page` defaults)
**Contributing files:**

View File

@@ -8,7 +8,7 @@ This skill is the conversational front door to the Gitea suite — it replaces t
## Usage
```
```text
/gitea-workflow
```

View File

@@ -13,7 +13,7 @@ The request names a capability but not obviously which skill owns it. Find the o
| `gitea-issues` | List/read/create/update issues, comments, search across issues and PRs. Composes `gitea-labels-milestones` for label/milestone resolution. |
| `gitea-labels-milestones` | Label and milestone CRUD, label inference from conversation context, resolving names/titles to the numeric IDs writes require. Cross-cutting — used by both `gitea-issues` and `gitea-prs`. |
| `gitea-prs` | List/read/create/update/merge PRs, code reviews. Composes `gitea-labels-milestones` the same way `gitea-issues` does. |
| `gitea-branches` | Branch list/create/delete, plus commit history (list commits, get a single commit by SHA). |
| `gitea-branches` | Branch list/create/rename/delete, plus commit history (list commits, get a single commit by SHA). |
| `gitea-files` | Read/write/delete individual files, list a directory, walk the full repo tree. |
| `gitea-releases` | Release and tag CRUD — draft/prerelease flags, release notes, semver tags. |