chore(gitea): regenerate the flat content mirror after the ADR-0020 retrofit

Generated output, not authored content: scripts/sync-plugin-content.sh --all.
Claude Code has no .apm/ awareness, so this compiled mirror must track .apm/ or
the check-plugin-content-sync pre-push hook reports drift.

Deferred to a single commit at the end of the wave on purpose. sync_dir runs
rm -rf before every copy, so running it while seven agents were editing the
same plugin would have raced them; agents were told not to sync for that reason.

Refs #99
This commit is contained in:
2026-08-30 12:42:20 +00:00
parent bedbd1d872
commit bbc73008a3
31 changed files with 407 additions and 353 deletions

View File

@@ -10,6 +10,18 @@ history (list commits, get a single commit's full detail) against a Gitea reposi
pagination conventions specific to Gitea's API (e.g. refusing to delete a protected branch without pagination conventions specific to Gitea's API (e.g. refusing to delete a protected branch without
explicit confirmation, and treating unexpected 404s as possible masked 403s). explicit confirmation, and treating unexpected 404s as possible masked 403s).
## Boundaries
This skill operates on the Gitea server via the MCP tools, never on your local checkout. Branch
and commit-history work against the working copy belongs to `git-branches` and `git-history`.
Branch references that only exist relative to a pull request — a PR's head or base branch, and
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",
"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 ## Before you start
Requires a Gitea MCP server configured with a token that has `write:repository` scope. This is Requires a Gitea MCP server configured with a token that has `write:repository` scope. This is

View File

@@ -2,22 +2,16 @@
name: gitea-branches name: gitea-branches
description: > description: >
Use when managing Gitea repository branches — listing, creating, or deleting Use when listing, creating, or deleting branches in a Gitea repository, or
branches — or inspecting commit history within a Gitea repo: listing commits reading its commit history — even when the user does not say "Gitea". Not a
(optionally filtered by branch or file path) or getting full detail for a local working copy's branches -> `git-branches`. Not local history ->
single commit by SHA. Triggers on "list branches", "create a branch", `git-history`. Not a PR's head or base branch -> `gitea-prs`.
"delete a branch", "what commits are on this branch", "show commit <sha>",
"what changed in that commit" — even if the user doesn't say "Gitea"
explicitly, as long as the repo's remote is a Gitea instance. Do not use for
local git branch/commit operations on your working copy (use git-branches or
git-history) or for PR-side branch references like cross-repo fork PR heads
(use 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. 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.
metadata: metadata:
category: integration category: integration
version: "0.1.1" version: "0.1.2"
source_keys: source_keys:
- gitea-mcp-repo - gitea-mcp-repo
- gitea-mcp-slim-go - gitea-mcp-slim-go
@@ -28,11 +22,9 @@ allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__git
## Gotchas ## Gotchas
- **Never delete a protected branch (`main`/`master` by name, or `protected: true` from `list_branches`) without explicit confirmation.** `delete_branch` is a direct API call, not a local `git push` — there is no client-side force-push guard protecting it. Name-matching `main`/`master` is a convenient default but not authoritative — a repo can protect a differently-named default branch. When in doubt, call `list_branches` first and check `protected` on the target; treat deletion of any protected branch as a hard refusal unless the user explicitly confirms in the conversation. - **404 often means 403.** Gitea masks permission errors as not-found; on an unexpected one, check token scope before reporting a branch or commit missing.
- **404 may actually mean 403.** Gitea hides permission errors as not-found to avoid leaking resource existence. If any of these five tools returns 404 unexpectedly, check token scope (see `references/branches.md` / `references/commits.md`) before concluding the branch or commit doesn't exist. - **Nothing auto-paginates.** `list_branches` and `list_commits` return one page; iterate `page` until the returned count is below `per_page`.
- **Pagination is manual.** `list_branches` and `list_commits` return one page at a time — no auto-pagination in the MCP layer. When you need a complete list, iterate `page: 1, 2, ...` until the returned count is less than `per_page`. - **`delete_branch` has no force-push guard.** Check `protected` from `list_branches` first and require explicit confirmation — a protected branch need not be named `main`.
- **Owner/repo always come from the git remote, never from `get_me`.** Resolve them via `git remote get-url origin` (Step 1 below). `get_me`/`list_my_repos` are blocked under the token scopes this skill assumes.
- **`create_branch`'s source is `old_branch`, not "wherever gitea-mcp feels like."** Omitting `old_branch` forks from the repo's server-side default branch — not necessarily the branch you're currently working on locally. If you want to branch from your current checkout, pass `old_branch` explicitly.
## Step 1 — Resolve owner and repo ## Step 1 — Resolve owner and repo
@@ -42,7 +34,7 @@ Before any tool call, extract `owner` and `repo` from the git remote:
git remote get-url origin 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." `get_me` and `list_my_repos` are blocked under the token scope this skill assumes, so the remote is the only source. 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 ## Step 2 — Dispatch
@@ -54,7 +46,7 @@ If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea r
| `/gitea-branches commits [on <branch>] [touching <path>]` | List commit history | | `/gitea-branches commits [on <branch>] [touching <path>]` | List commit history |
| `/gitea-branches commit <sha>` | Get full detail for one commit | | `/gitea-branches commit <sha>` | Get full detail for one commit |
For branch operations (list/create/delete), read `references/branches.md`. 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 commit operations (list/get), read `references/commits.md`. For commit operations (list/get), read `references/commits.md`.
## Step 3 — Report ## Step 3 — Report
@@ -63,4 +55,4 @@ For reads: display branches as name + protected flag; display commits as SHA (sh
For writes (create/delete): confirm the action taken, the branch name, and (for create) the base it forked from. For writes (create/delete): confirm the action taken, the branch name, and (for create) the base it forked from.
For errors: surface the HTTP code and message. If a 404 is unexpected, re-check token scope per the Gotchas above before reporting "not found" to the user. For errors: surface the HTTP code and message, applying the 404 gotcha above before reporting "not found" to the user.

View File

@@ -65,7 +65,7 @@ A branch name collision returns `409 Conflict`.
delete_branch owner: <owner> repo: <repo> branch: <name> delete_branch owner: <owner> repo: <repo> branch: <name>
``` ```
Before calling this, see the hard-refusal Gotcha in SKILL.md. If the target branch's name isn't Before calling this, see the `delete_branch` Gotcha in SKILL.md. If the target branch's name isn't
obviously a scratch/feature branch, call `list_branches` first and check `protected` on the obviously a scratch/feature branch, call `list_branches` first and check `protected` on the
matching entry — name-matching `main`/`master` alone isn't authoritative, since a repo can protect matching entry — name-matching `main`/`master` alone isn't authoritative, since a repo can protect
a differently-named default branch. Confirm explicitly with the user before deleting anything a differently-named default branch. Confirm explicitly with the user before deleting anything

View File

@@ -42,7 +42,7 @@ Dispatch defaults:
**Response:** one object per commit: `sha`, `html_url`, `created`, `message` (when available), **Response:** one object per commit: `sha`, `html_url`, `created`, `message` (when available),
`author` (`{name, email, date}`, when available). `author` (`{name, email, date}`, when available).
Paginate per the manual-pagination Gotcha in SKILL.md if you need more than one page of history. Paginate per the pagination Gotcha in SKILL.md if you need more than one page of history.
## `get_commit` ## `get_commit`

View File

@@ -19,5 +19,6 @@ Describe the file task: read a file or directory, walk a tree, create/update a f
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `SKILL.md` | Skill instructions for agents | | `SKILL.md` | Skill instructions for agents |
| `references/examples.md` | Canonical call sequences: branch + file + PR, recovering a missing SHA before an update, deleting a file | | `references/reading.md` | Loaded for the read flow: the three read tools, `ref`/`tree_sha` selection, tree pagination, and why a listing is not a SHA source |
| `references/writing.md` | Loaded for the write flow: the SHA-first create/update/delete sequences, `new_branch_name`, the worked branch + file + PR sequence, and failed-write triage |
| `references/sources.md` | Research sources backing the SHA/concurrency and direct-commit-vs-PR guidance | | `references/sources.md` | Research sources backing the SHA/concurrency and direct-commit-vs-PR guidance |

View File

@@ -2,15 +2,9 @@
name: gitea-files name: gitea-files
description: > description: >
Use when reading or writing individual files or directory trees in a Gitea repository via the Use when reading or writing files in a Gitea repository rather than on the local filesystem —
Gitea MCP server: reading a file's contents, listing a directory, walking a full repository read, list, walk the tree, create, update, or delete — even when the user does not say "Gitea".
tree, creating a new file, updating an existing file, or deleting a file. Triggers on "read this Not commit history -> `gitea-branches`. Not pull requests -> `gitea-prs`.
file from the repo", "what's in this directory", "show me the repo tree", "create/update a file
in Gitea", "commit this file to the branch", "delete this file from the repo" — even when the
user doesn't say "Gitea" explicitly, as long as the target is a Gitea-hosted repository. Do not
use for local filesystem file operations (use Read/Write/Edit), for branch or commit history
(use gitea-branches), or for opening a pull request around a file change (use gitea-prs after
the file write completes here).
compatibility: Requires the Gitea MCP server configured with a token scoped to at least compatibility: Requires the Gitea MCP server configured with a token scoped to at least
write:repository. Tested with a token holding write:issue + write:repository; write:issue write:repository. Tested with a token holding write:issue + write:repository; write:issue
@@ -28,29 +22,27 @@ allowed-tools: mcp__gitea__get_file_contents mcp__gitea__get_dir_contents mcp__g
## Gotchas ## Gotchas
- **A 404 from any read call may actually be a 403 in disguise.** `get_file_contents`, `get_dir_contents`, and `get_repository_tree` all gate on `write:repository` scope, not just read access — some Gitea endpoints return 404 instead of 403 when the token's scope is insufficient, to avoid leaking whether the resource exists. If a read fails with 404 on a path you're confident is correct, check the token's configured scopes before concluding the file or directory doesn't exist. - **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.
- **SHA is the concurrency token for every write — and it lives at the top level of `get_file_contents`'s response, not nested under `content`.** `create_or_update_file` without `sha` is always treated as a *create*: if the path already exists, Gitea returns HTTP 409. `delete_file` has no optional path at all — omitting `sha` returns HTTP 422. The safe sequence for any update or delete is always: call `get_file_contents` first, read the top-level `sha` field, then pass that exact value to the write call. Never guess or reuse a stale SHA — a mismatched SHA is rejected the same as a missing one. - **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.
- **A write can also fail because the branch requires signed commits — a separate failure mode from a bad SHA.** `create_or_update_file` and `delete_file` create commits server-side via a bare API token call with no 2FA/PGP context. If the target branch's protection rule requires signed commits, Gitea rejects the write outright — surfaced as a generic 403 or 422, not an error naming "signed commit required," and reads against that same branch keep succeeding right up until you try to write. When a write fails without a clean 409 (missing/stale SHA) or 404 (bad path) explanation, check whether the branch's protection rule requires signed commits before assuming the SHA is wrong and retrying. - **`content` is base64 both ways.** Encode before a write, decode after a read; `withLines: true` returns numbered lines.
- **A large `create_or_update_file` payload can hit a reverse-proxy 413 that has nothing to do with Gitea.** `content` is base64-encoded, which inflates the payload ~33% over the raw file size; a 413 is commonly a reverse-proxy body-size limit in front of the Gitea instance, not a Gitea-side rejection. No amount of retrying, or changing the SHA, path, or branch, will fix it — it needs the proxy's config raised, which is outside this skill's or the calling agent's control. Surface that distinction to the user instead of retrying the same call.
- **`get_dir_contents` and `get_repository_tree` are not SHA sources for a specific file's write.** `get_dir_contents` entries carry no `sha` at all. `get_repository_tree` entries do carry a `sha` (a blob/tree hash), but fetching it means an extra round trip with no content — `get_file_contents` is the canonical path since it returns the decoded content and the write-ready `sha` in one call.
- **`owner` and `repo` are always caller-supplied inputs, never resolved here.** This skill doesn't infer them from a git remote. If invoked directly by a human, ask for them if not stated. If invoked by `gitea-workflow` or an orchestrating agent, expect them to already be resolved and passed in.
- **Direct commits to a branch are a first-class action, not a workaround.** Gitea's own web UI defaults to editing files directly against a branch — `create_or_update_file`/`delete_file` used that way is normal, not an API escape hatch to avoid. The SHA-currency requirement above is the actual risk to manage, not the act of committing directly.
- **`ref` (reads) vs. `branch_name` (writes) are different parameters for the same concept.** `get_file_contents`, `get_dir_contents`, and `get_repository_tree` (as `tree_sha`) all accept a branch name, tag, or commit SHA to select what to read. `create_or_update_file` and `delete_file` instead take `branch_name` — the branch the commit lands on. Don't conflate the two when chaining a read into a write.
- **Content is base64.** `create_or_update_file`'s `content` parameter is base64-encoded file content, not raw text — encode before calling. `get_file_contents`'s response content is likewise base64-encoded (decode after reading), unless `withLines: true` is passed for a numbered-line view.
## Reading ## Inputs
- **Single file:** `get_file_contents(owner, repo, ref, path)`. Pass `withLines: true` only when you need line numbers for referencing specific lines (e.g. quoting a snippet back to the user); omit it for a normal content fetch. `owner`, `repo` and the target branch are caller-supplied. This skill never infers them from a git remote: ask the human when they are not stated, and expect an orchestrating caller to have resolved them already.
- **One directory level:** `get_dir_contents(owner, repo, ref, path)` — returns immediate entries only (name, path, type, size), no recursion, no SHA, no content.
- **Whole tree:** `get_repository_tree(owner, repo, tree_sha, recursive)` — `tree_sha` accepts a SHA, branch, or tag name despite the name. Set `recursive: true` to walk subdirectories in one call. Response includes `truncated: true` when a page doesn't hold every entry — page through with `page`/`per_page` (default `page: 1`, `per_page: 30`) until you get fewer results than `per_page`.
## Writing ## Dispatch
- **Creating a new file:** call `create_or_update_file(owner, repo, path, content, message, branch_name)` with `sha` omitted entirely. | Condition | Flow | Reference |
- **Updating an existing file:** call `get_file_contents(owner, repo, ref: branch_name, path)` first, take the top-level `sha`, then call `create_or_update_file(..., sha: <that value>)`. |---|---|---|
- **Deleting a file:** call `get_file_contents` first the same way, then `delete_file(owner, repo, path, message, branch_name, sha: <that value>)` — `sha` is required, no create-style fallback exists. | Read one file, list one directory level, or walk the repository tree | Read | `references/reading.md` |
- **Creating a new branch as part of the write:** pass `new_branch_name` on `create_or_update_file` to branch off before the commit lands, instead of calling a separate branch-creation step. | Create, update, or delete a file | Write | `references/writing.md` |
If the change needs review before merging, or targets a protected branch, hand off to `gitea-prs` after the write lands here to open the pull request — this skill's scope ends at the commit. 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 you need the full multi-call sequence rather than the single-call summary above — e.g. branching off as part of a file push ahead of opening a PR, or recovering a SHA you didn't capture earlier — read `references/examples.md`. 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.
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.
## Handoff
Scope ends at the commit. Gitea's own web UI edits files directly against a branch, so committing straight to a branch is the normal path rather than an escape hatch — hand off to `gitea-prs` when the change needs review before merging or the target branch is protected, not by default.

View File

@@ -1,65 +0,0 @@
---
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Canonical call sequences
## Push a file to a new branch, then open a PR
```
1. get_repository_tree or get_file_contents on the base branch — only needed if the
new file is actually replacing an existing one; skip for a brand-new path.
2. create_or_update_file
owner, repo
path: "docs/example.md"
content: "<base64-encoded content>"
message: "docs: add example"
branch_name: "main"
new_branch_name: "feat/add-example" ← branches off before the commit lands
(sha omitted — this is a new file)
3. Hand off to gitea-prs to open a PR from "feat/add-example" into "main".
```
`new_branch_name` on `create_or_update_file` replaces a separate branch-creation call — the branch is created and the commit lands on it in one step.
## Update a file when you don't already have its SHA
SHA is mandatory for updates. If it wasn't captured earlier in the conversation:
```
1. get_file_contents
owner, repo
ref: "main"
path: "docs/example.md"
→ read the top-level `sha` field (not content.sha)
2. create_or_update_file
owner, repo
path: "docs/example.md"
content: "<new base64-encoded content>"
message: "docs: update example"
branch_name: "main"
sha: "<sha from step 1>"
```
Do not guess or omit the SHA — the write either fails (409 on create-path fallback) or is rejected outright.
## Delete a file
Same SHA-first pattern, no fallback path:
```
1. get_file_contents owner, repo, ref: "main", path: "docs/old-example.md"
→ read the top-level `sha` field
2. delete_file
owner, repo
path: "docs/old-example.md"
message: "docs: remove old example"
branch_name: "main"
sha: "<sha from step 1>"
```

View File

@@ -0,0 +1,46 @@
---
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Reading files, directories and trees
All three read calls select what to read with `ref` — a branch name, tag, or commit SHA. On
`get_repository_tree` the same value goes in `tree_sha` despite the name.
## Single file
`get_file_contents(owner, repo, ref, path)`.
The response carries the file's `sha` at the **top level**, not nested under `content`. That field
is the write-ready SHA, so capture it whenever a write may follow. Content comes back
base64-encoded — decode it. Pass `withLines: true` only when you need numbered lines to quote
specific lines back to the user; omit it for a normal content fetch.
## One directory level
`get_dir_contents(owner, repo, ref, path)` returns the immediate entries only — name, path, type,
size. No recursion, no content, no `sha`.
## Whole repository tree
`get_repository_tree(owner, repo, tree_sha, recursive)`. Set `recursive: true` to walk
subdirectories in one call.
The response sets `truncated: true` when one page does not hold every entry. Page through with
`page`/`per_page` (defaults `1` and `30`) until a page returns fewer entries than `per_page`.
## Neither listing is a SHA source for a write
`get_dir_contents` entries carry no `sha` at all. `get_repository_tree` entries do carry a blob or
tree hash, but reaching it costs an extra round trip and returns no content. `get_file_contents` is
the canonical path for a write's SHA: one call returns the decoded content and the write-ready
`sha` together.
## A 404 that is really a 403
These reads gate on `write:repository`, not on read access alone, and some Gitea endpoints answer
an under-scoped token with 404 instead of 403 so they do not leak whether the resource exists. A
404 on a path you are confident about is a scope problem until proven otherwise — check the token's
configured scopes before concluding the file or directory does not exist.

View File

@@ -8,9 +8,10 @@
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
**Contributing files:** **Contributing files:** (tool parameters and SHA/concurrency behavior cross-checked live against the deployed MCP tool schemas via ToolSearch)
- SKILL.md (Gotchas, Reading, Writing — tool parameters and SHA/concurrency behavior, cross-checked live against the deployed MCP tool schemas via ToolSearch) - SKILL.md (Gotchas — cross-flow parameter and encoding traps; Dispatch)
- references/examples.md (canonical call sequences) - references/reading.md (read-tool parameters, `ref`/`tree_sha` selection, tree pagination)
- references/writing.md (write-tool parameters, SHA/concurrency behavior, canonical call sequences, failed-write triage)
## gitea-mcp-slim-go ## gitea-mcp-slim-go
@@ -21,8 +22,9 @@
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
**Contributing files:** **Contributing files:**
- SKILL.md (Gotchas — top-level `sha` field location, `get_dir_contents`/`get_repository_tree` not being usable SHA sources for a file write) - SKILL.md (Gotchas — base64 response encoding)
- references/examples.md (SHA-first update/delete sequences) - references/reading.md (top-level `sha` field location, `get_dir_contents`/`get_repository_tree` not being usable SHA sources for a file write)
- references/writing.md (SHA-first update/delete sequences)
## context7-websites-gitea ## context7-websites-gitea
@@ -33,7 +35,7 @@
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
**Contributing files:** **Contributing files:**
- SKILL.md (Gotchas — "Direct commits to a branch are a first-class action, not a workaround") - SKILL.md (Handoff — committing straight to a branch is the normal path, not an escape hatch)
## context7-gitea-tea-cli ## context7-gitea-tea-cli

View File

@@ -0,0 +1,76 @@
---
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Creating, updating and deleting files
`sha` is the optimistic-concurrency token for every write, and it comes from
`get_file_contents`'s **top-level** `sha` field — never from `content.sha`, a directory listing, or
a tree entry. Do not guess or reuse a stale value: a mismatched SHA is rejected exactly like a
missing one.
`content` is base64-encoded, and the branch the commit lands on is `branch_name`, not `ref`.
## Create a new file
Call `create_or_update_file(owner, repo, path, content, message, branch_name)` with `sha` omitted
entirely. An omitted `sha` always means *create*, so the call returns HTTP 409 if the path already
exists.
To branch off as part of the same write, pass `new_branch_name`: the branch is created and the
commit lands on it in one call, replacing a separate branch-creation step.
## Update an existing file
1. `get_file_contents(owner, repo, ref: <branch>, path)` → read the top-level `sha`.
2. `create_or_update_file(owner, repo, path, content, message, branch_name, sha: <that value>)`.
## Delete a file
Same SHA-first pattern, with no create-style fallback — `delete_file` without `sha` returns
HTTP 422.
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
```
1. create_or_update_file
owner, repo
path: "docs/example.md"
content: "<base64-encoded content>"
message: "docs: add example"
branch_name: "main"
new_branch_name: "feat/add-example" ← branches off before the commit lands
(sha omitted — this is a new file)
2. Hand off to gitea-prs to open a PR from "feat/add-example" into "main".
```
Step 1 needs no preceding read: a brand-new path has no SHA. Fetch the current file first only
when the write replaces an existing one.
## Triaging a failed write
| 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 |
| 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 |
| 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
PGP context. If the target branch's protection rule requires signed commits, Gitea rejects the
write as a generic 403 or 422 that never names signing, and reads against that same branch keep
succeeding right up until the write. When a write fails without a clean 409 or 404 explanation,
check the branch's protection rule before assuming the SHA is wrong and retrying.
**Payload size.** base64 inflates `content` roughly 33% over the raw file size, and a 413 is
usually a reverse-proxy body-size limit in front of the Gitea instance rather than a Gitea-side
rejection. No amount of retrying, or changing the SHA, path, or branch, will fix it — the proxy's
config has to be raised, which is outside this skill's control. Surface that distinction instead
of retrying the same call.

View File

@@ -18,9 +18,19 @@ the blocked `get_me` scope, and the "Depends on #N" dependency-linking conventio
Requires a Gitea MCP server configured with a token holding `write:issue` + `write:repository`. 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 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. Label and milestone management (e.g. `gitea-workflow`) already resolved `owner`/`repo` for you.
(creating/editing a label, creating/closing a milestone) is out of scope here — that's
`gitea-labels-milestones`, which this skill composes rather than duplicates. ## How it composes
This skill composes `gitea-labels-milestones` for *all* label inference, label-name-to-ID
resolution, and milestone lookup, rather than duplicating that taxonomy or its resolution logic —
see `references/enrichments.md` for the call protocol. Managing the label and milestone definitions
themselves (create/edit/delete a label, create/close a milestone) is out of scope here and goes to
`gitea-labels-milestones` directly.
One boundary the description does not spend characters on, because it was never going to win an
issue request: local git branch or commit work belongs to `gitea-branches` (Gitea-side) or
`git-branches` (working copy).
## Usage ## Usage
@@ -28,8 +38,8 @@ Requires a git remote named `origin` pointing at the Gitea instance, unless an o
/gitea-issues /gitea-issues
``` ```
Describe your task: list issues, create one, get/comment/close a specific issue number, or search Describe your task: list issues, create one, get/comment/close/label a specific issue number, or
across repos. See `SKILL.md`'s dispatch table for the full set of recognized invocations. search across repos. See `SKILL.md`'s dispatch table for the full set of recognized invocations.
## Files ## Files

View File

@@ -2,15 +2,9 @@
name: gitea-issues name: gitea-issues
description: > description: >
Use when reading or writing Gitea issues: listing repo issues, getting a single issue's details/ Use when reading or writing Gitea issues — even when the user does not say "Gitea".
comments/labels, creating an issue, updating its state, adding or editing comments, applying Not pull requests -> `gitea-prs`.
labels via issue_write, or searching issues/PRs across repositories. Triggers on "create an Not label or milestone definitions -> `gitea-labels-milestones`.
issue", "what issues are open", "get issue #N", "close issue #N", "comment on issue #N", "search
issues for X" — even when the user doesn't say "Gitea" explicitly. Composes gitea-labels-
milestones for all label inference/resolution and milestone lookup — do not use this skill to
manage label or milestone definitions themselves (create/edit/delete a label, create/close a
milestone), that's gitea-labels-milestones directly. Do not use for pull requests (use gitea-prs)
or for local git branch/commit work (use gitea-branches or git-branches).
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token 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 scopes. Requires git remote "origin" pointing to the Gitea instance for owner/repo resolution
@@ -19,7 +13,7 @@ compatibility: Requires Gitea MCP server configured with write:issue and write:r
metadata: metadata:
category: integration category: integration
version: "0.1.0" version: "0.1.3"
source_keys: source_keys:
- gitea-mcp-repo - gitea-mcp-repo
- gitea-mcp-slim-go - gitea-mcp-slim-go
@@ -31,75 +25,49 @@ allowed-tools: Bash mcp__gitea__list_issues mcp__gitea__issue_read mcp__gitea__i
## Gotchas ## Gotchas
- **`list_issues` has no `type` or `milestones` parameter — despite `api-reference.md` documenting both.** The live MCP schema (re-verified via `ToolSearch` at authoring time — see `references/sources.md`) only accepts `owner`, `repo` (required), `state` (default `"all"`), `labels` (array of label *names*), `since`, `before` (ISO 8601), `page`, `per_page` (default 30). This tool provides no way to filter issues-vs-PRs or by milestone. Since issues and PRs share one number space, `list_issues` results can include PR entries with no client-side filter to exclude them. If you need to know whether a specific number is a PR, call `issue_read method: "get"` and check `is_pull` — that field only appears on the single-item response, never in a list item. This exact drift (a prior skill trusted the research doc's `type` param and broke) is why this skill's reference files were re-verified live rather than copied from `api-reference.md`. - **`list_issues` returns PRs too.** It has no `type` filter and both share one repo number space. `is_pull` appears only on `issue_read method: "get"`, never on a list item — check it there before treating a number as an issue.
- **`search_issues` does have a working `type` filter** (`"issues"` | `"pulls"`) — unlike `list_issues`. Its `labels` parameter is also shaped differently: a comma-separated string, not an array of names. - **Label IDs and names are not interchangeable.** `issue_write` takes numeric IDs only; `list_issues` and `search_issues` filter by name; `issue_read "get"` returns names but `"get_labels"` returns full objects with IDs. Resolve via `gitea-labels-milestones` unless the caller named exact labels.
- **Labels are numeric IDs on write, name strings on read.** `issue_write`'s `labels` parameter (used by `add_labels`/`replace_labels`) takes IDs. `list_issues`/`issue_read` return names. Never resolve this yourself — compose `gitea-labels-milestones` (see `references/enrichments.md`) to get IDs. - **A merged PR leaves its issue open.** Gitea does not auto-close on merge the way GitHub does. Re-read the issue's state after a merge before closing it manually.
- **Milestone on `issue_read` is `{id, title}`** — an object, not a bare string. This skill only ever needs the `id`. (The bare-title-string case only happens on the PR side, which is `gitea-prs`' problem, not this skill's.) - **A 404 may really be a 403.** Gitea hides permission errors as not-found — check the token's `write:issue` scope before concluding the issue does not exist.
- **Closing-keyword auto-close behavior is plausible but unconfirmed in our research docs.** Our research docs confirm Gitea does NOT auto-close an issue on a plain PR merge (unlike GitHub) — closing keywords like `Fixes #N`/`Closes #N` in a commit message are not documented one way or the other. After a PR merges (a `gitea-prs` operation), always re-check the issue's state here via `issue_read method: "get"` before deciding whether to close it manually — closing an already-closed issue is a harmless no-op, but don't assume a manual close is always needed.
- **Pagination is manual.** `list_issues` and `search_issues` return one page at a time. Iterate `page: 1, 2, ...` until the returned count is less than `per_page`.
- **HTTP 404 may actually mean 403.** Gitea hides permission errors as not-found. If a call 404s unexpectedly, verify the token holds `write:issue` scope (see `references/issues.md`'s Token scope note) before concluding the issue doesn't exist.
## Step 1 — Resolve owner and repo ## Step 1 — Resolve owner and repo
Before any tool call, extract `owner` and `repo` from the git remote (skip this if an orchestrating caller already passed them in): An orchestrating caller may pass `owner` and `repo` in already; if so, skip this. The `search` row is cross-repository and needs only a query, so it skips this too. Otherwise, before any tool call:
```bash ```bash
git remote get-url origin 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." If origin is unset 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 ## Step 2 — Dispatch
| Invocation | Action | One invocation takes one row. Read only the reference(s) that row names — the call signatures were verified against the live MCP schema and differ from the published API docs in ways the body does not restate.
|---|---|
| `/gitea-issues` or `/gitea-issues list` | List issues (optional state filter) |
| `/gitea-issues create` | Create an issue from conversation context — infers labels, checks milestone fit, applies a configured default assignee if set |
| `/gitea-issues <N>` | Get issue details (flag it as a PR if `is_pull: true`) |
| `/gitea-issues <N> comments` | Get an issue's comments |
| `/gitea-issues close <N>` | Close an issue |
| `/gitea-issues comment <N>` | Add a comment from conversation context |
| `/gitea-issues search <query>` | Cross-repo search via `search_issues` |
For full parameter detail on `list_issues`/`issue_read`/`issue_write`, read `references/issues.md`. For `search_issues`, read `references/search.md`. For the create-flow enrichments (label inference, milestone assignment, assignee workaround, dependency-linking), read `references/enrichments.md`. | Invocation | Flow | Read |
|---|---|---|
| `/gitea-issues` or `/gitea-issues list` | List issues, optionally filtered by state | `references/issues.md` |
| `/gitea-issues <N>` | Get one issue, routing to `gitea-prs` when the number turns out to be a PR | `references/issues.md` |
| `/gitea-issues <N> comments` | Get an issue's comments | `references/issues.md` |
| `/gitea-issues <N> labels` | Get an issue's labels as full objects, IDs included | `references/issues.md` |
| `/gitea-issues close <N>` | Close an issue by updating its state | `references/issues.md` |
| `/gitea-issues comment <N>` | Add a comment drawn from conversation context, never one invented to fill the gap — a comment on a live issue is not cheap to undo | `references/issues.md` |
| `/gitea-issues label <N>` | Apply, replace or remove labels using IDs resolved by `gitea-labels-milestones` | `references/issues.md` |
| `/gitea-issues create` | Create an issue — Step 3 first | `references/enrichments.md`, then `references/issues.md` |
| `/gitea-issues search <query>` | Cross-repo search, narrowed by owner, state, type or labels | `references/search.md` |
## Step 3 — Execute ## Step 3 — Create
### list (default) Only the create flow reaches this step; every other row goes straight to its reference.
Call `list_issues owner: <owner> repo: <repo> state: <"open"|"closed"|"all", default "all">`. Remember: results may include PR entries (see Gotchas) — if the caller needs issues only, this tool cannot filter that server-side; note the limitation rather than silently mislabeling PR entries as issues. 1. Take `title` and `body` from conversation context — the most recent task, bug report, or explicit statement. An empty body is an acceptable fallback, an invented one is not.
2. Run the enrichments in `references/enrichments.md`, then create with the resolved IDs per `references/issues.md`. Omitting a parameter always beats guessing its value — a wrong milestone or assignee is harder to notice than a missing one.
### create 3. Fire immediately, with no confirmation step. A create is cheap to undo by closing, so a gate here only costs a round trip.
1. Extract `title` and `body` from conversation context (the most recent task, bug description, or explicit statement). Fall back to an empty body if nothing is available.
2. Run the enrichment sequence in `references/enrichments.md`: infer labels (composing `gitea-labels-milestones`), check for a clearly-fitting open milestone (composing the same skill), and check for a configured default assignee.
3. Call `issue_write method: "create" owner: <owner> repo: <repo> title: <title> body: <body> labels: [<resolved IDs, or omit>] milestone: <resolved ID, or omit> assignees: [<default login, or omit>]`.
4. Fire immediately — no confirmation step for the create itself.
### `<N>` (get)
Call `issue_read method: "get" owner: <owner> repo: <repo> issue_number: <N>`. If `is_pull: true`, report that this number is actually a PR and suggest `gitea-prs` for full detail.
### `<N> comments`
Call `issue_read method: "get_comments" owner: <owner> repo: <repo> issue_number: <N>`.
### close `<N>`
Call `issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed"`. No `method: "close"` exists.
### comment `<N>`
Extract the comment body from conversation context (same sourcing as create). Call `issue_write method: "add_comment" owner: <owner> repo: <repo> issue_number: <N> body: <body>`.
### search `<query>`
Call `search_issues query: <query>`, adding `owner`, `state`, `type`, or `labels` filters if the request narrows scope (e.g. "search open PRs for X" → `type: "pulls" state: "open"`).
## Step 4 — Report ## Step 4 — Report
For reads: a compact table or numbered list — number, title, state, labels, milestone. Reads: a compact table or numbered list — number, title, state, labels, milestone.
For writes: confirm what was created/updated with the issue number and URL if returned. Writes: what was created or updated, with the issue number and the URL when one is returned.
For errors: surface the HTTP code and message; check token scope per the Gotchas if a 404 looks wrong. Errors: the HTTP code and message as returned, without paraphrasing either.

View File

@@ -19,8 +19,7 @@ plausibly fits).
reference files directly by path. A plugin install copies each skill's directory into an isolated reference files directly by path. A plugin install copies each skill's directory into an isolated
cache — any file path that leaves this skill's own directory breaks post-install. Instead, compose cache — any file path that leaves this skill's own directory breaks post-install. Instead, compose
`gitea-labels-milestones` as a skill: describe the task to it (its own `SKILL.md` and description `gitea-labels-milestones` as a skill: describe the task to it (its own `SKILL.md` and description
trigger it) and consume the resolved IDs it returns. This mirrors how `gitea-labels-milestones`'s trigger it) and consume the resolved IDs it returns.
own description already frames the relationship ("`gitea-issues` and `gitea-prs` both compose it").
## 1. Label inference ## 1. Label inference

View File

@@ -5,8 +5,8 @@ signatures in `references/issues.md` and `references/search.md` were re-verified
`ToolSearch` against the deployed `gitea-mcp` server at authoring time — they are not copied `ToolSearch` against the deployed `gitea-mcp` server at authoring time — they are not copied
verbatim from `api-reference.md`. This resolves issue #6 comment #849's root-cause finding that a verbatim from `api-reference.md`. 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 live prior skill was authored from API docs that had drifted from the actual MCP tool schema; the live
check caught exactly this drift on `list_issues` (see SKILL.md Gotchas — the research doc documents check caught exactly this drift on `list_issues` (see `references/issues.md` — the research doc
a `type` and a `milestones` parameter that do not exist on the deployed server). documents a `type` and a `milestones` parameter that do not exist on the deployed server).
## gitea-mcp-repo ## gitea-mcp-repo

View File

@@ -4,7 +4,13 @@ Read and write Gitea labels and milestones, and resolve label/milestone identity
## What it does ## What it does
This skill handles label and milestone CRUD (`label_read`/`label_write`, `milestone_read`/`milestone_write`) — listing repo or org labels, creating/editing/deleting a label, resolving a label name to the numeric ID required for any write, and listing/creating/updating/closing/deleting a milestone. It also owns label inference: mapping conversation context (bug report, feature request, urgency language) to this repo's `Kind/*`/`Priority/*`/`Status/*` taxonomy. It is a cross-cutting shared skill composed by `gitea-issues` and `gitea-prs`, which call into it for label/milestone resolution before their own `issue_write`/`pull_request_write` calls apply the resolved IDs. This skill handles label and milestone CRUD (`label_read`/`label_write`, `milestone_read`/`milestone_write`) — listing repo or org labels, creating/editing/deleting a label, resolving a label name to the numeric ID required to apply it to an issue or PR, and listing/creating/updating/closing/deleting a milestone. It also owns label inference: mapping conversation context (bug report, feature request, urgency language) to this repo's `Kind/*`/`Priority/*`/`Status/*` taxonomy.
## Composition
This is a cross-cutting shared skill. `gitea-issues` and `gitea-prs` both compose it whenever they need to apply a label or assign a milestone, rather than duplicating label/milestone logic: they call in for name/title → ID resolution, then their own `issue_write`/`pull_request_write` calls apply the resolved IDs. The split is deliberate — identity resolution lives here once, and the write that attaches an ID to a specific issue or PR lives with the skill that owns that object.
That relationship is documented here rather than in the skill description, which is preloaded into every session and carries routing information only: an agent reaches this skill because the user asked about labels or milestones, not because two other skills call it.
## Usage ## Usage

View File

@@ -2,15 +2,9 @@
name: gitea-labels-milestones name: gitea-labels-milestones
description: > description: >
Use when reading or writing Gitea labels or milestones — listing repo/org labels, creating, Use when reading or writing Gitea labels or milestones — resolve names to IDs, or infer
editing, or deleting a label, resolving label names to the numeric IDs required for applying labels — even when the user does not say "Gitea".
them to an issue or PR, or listing, creating, updating, closing, or deleting a milestone. This is Not applying them to an issue -> `gitea-issues`. Not to a PR -> `gitea-prs`.
a cross-cutting shared skill: `gitea-issues` and `gitea-prs` both compose it whenever they need to
apply labels or assign a milestone, rather than duplicating label/milestone logic. Also use for
label inference — mapping a bug report, feature request, or urgency signal in conversation
context to the repo's `Kind/*`/`Priority/*`/`Status/*` label taxonomy. Do not use for applying
already-resolved label IDs or milestone IDs to a specific issue or PR — that write goes through
`issue_write`/`pull_request_write` in `gitea-issues`/`gitea-prs`, not here.
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.
@@ -21,23 +15,18 @@ metadata:
- gitea-mcp-slim-go - gitea-mcp-slim-go
- context7-websites-gitea - context7-websites-gitea
- context7-gitea-tea-cli - context7-gitea-tea-cli
version: "0.1.1" version: "0.1.4"
allowed-tools: Bash mcp__gitea__label_read mcp__gitea__label_write mcp__gitea__milestone_read mcp__gitea__milestone_write allowed-tools: Bash mcp__gitea__label_read mcp__gitea__label_write mcp__gitea__milestone_read mcp__gitea__milestone_write
--- ---
## Gotchas ## Gotchas
- **Label writes take IDs, reads return names.** `label_read` is the only tool that returns full label objects (`id`, `name`, `color`, `description`). Issue/PR responses slim labels down to name strings. Before any label is applied to an issue or PR (in `gitea-issues`/`gitea-prs`), resolve names → IDs here via `label_read method: "list_repo_labels"` — never pass a name string where an ID is expected. - **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`.
- **Milestones are referenced by ID everywhere, never by title.** `milestone_write` update/delete take `id`. The one place titles show up as the sole handle is the `pull_request_read` response (see next gotcha). - **`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.
- **Milestone representation differs between issues and PRs.** `issue_read` returns `milestone: {id, title}` — an object. `pull_request_read` returns `milestone: "title string"` — title only, no ID. You cannot recover a milestone ID from a PR response directly; call `milestone_read method: "list"` and match by title instead. - **`Kind/*`/`Priority/*`/`Status/*` exclusivity is a client-side convention.** `exclusive` is an org-labels-only flag, so applying a label in such a scope must replace the one already there, not stack on it.
- **Repo labels and org labels are separate pools, never mixed in one call.** `label_read`/`label_write` take either `owner`+`repo` (repo-scoped methods) or `org` (org-scoped methods) — passing both or neither for a given method is a caller error, not something the schema enforces for you. Repo and org labels can both apply to the same issue, but you list/create/edit them through different method values.
- **`milestone_write` accepts `"update"` and `"edit"` as the same operation.** Both method values map to the identical update call. Prefer `"update"` for consistency with `issue_write`/`pull_request_write`.
- **`exclusive` is documented as an org-labels-only flag — it isn't what enforces exclusivity here.** Gitea's docs scope the settable/server-enforced `exclusive` flag to org labels only, and the live `label_write` schema for `create_repo_label`/`edit_repo_label` doesn't document accepting it at all. This repo's `Kind/*`, `Priority/*`, `Status/*` groups still behave as one-label-per-scope, but that's a manually-enforced convention this skill implements client-side, not a guaranteed server behavior for repo labels: applying a new label within a scope (e.g. `Priority/High`) must replace any existing label in that same scope, not add alongside it, and nothing on the server enforces that for you. Label inference (see `references/label-inference.md`) must respect this — replace, don't stack.
- **Pagination is manual on every list call.** `label_read` and `milestone_read` both default to `per_page: 30`. Iterate `page: 1, 2, ...` until the result count is less than `per_page` — there is no cursor or auto-pagination.
- **Schema requiredness differs between the two tool families.** `milestone_read`/`milestone_write` have `owner` and `repo` as hard-required parameters (the call fails validation without them). `label_read`/`label_write` only hard-require `method` — `owner`/`repo`/`org` are functionally required per method but not schema-enforced, so passing none produces a runtime error from Gitea, not a client-side validation error.
## Step 1 — Resolve owner and repo ## Step 1 — Resolve owner, repo and org
Before any tool call, extract `owner` and `repo` from the git remote (skip this if an orchestrating caller already passed them in): Before any tool call, extract `owner` and `repo` from the git remote (skip this if an orchestrating caller already passed them in):
@@ -47,10 +36,13 @@ 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." 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."
The `*_org_label*` methods take `org`, not `owner`/`repo`. Pass that same `owner` as `org` — it is the org name whenever the owner is an organisation, and the remote URL does not say whether it is one. So let the call itself decide: a failure means the owner is a user account with no org label pool, which is an answer, not an error to report.
## Step 2 — Dispatch ## Step 2 — Dispatch
| Task | Tool | method | | Task | Tool | method |
|---|---|---| |---|---|---|
| Resolve a label name to its ID | `label_read` | `"list_repo_labels"`, then `"list_org_labels"` |
| List repo labels | `label_read` | `"list_repo_labels"` | | List repo labels | `label_read` | `"list_repo_labels"` |
| Get one repo label by ID | `label_read` | `"get_repo_label"` | | Get one repo label by ID | `label_read` | `"get_repo_label"` |
| List org labels | `label_read` | `"list_org_labels"` | | List org labels | `label_read` | `"list_org_labels"` |
@@ -63,6 +55,6 @@ If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea r
| Update / close a milestone | `milestone_write` | `"update"` | | Update / close a milestone | `milestone_write` | `"update"` |
| Delete a milestone | `milestone_write` | `"delete"` | | Delete a milestone | `milestone_write` | `"delete"` |
For full parameter detail and step-by-step call sequences, read `references/labels.md` (label operations) or `references/milestones.md` (milestone operations). For mapping conversation context to a label to apply, read `references/label-inference.md`. Every list method paginates manually — `per_page` defaults to 30, so iterate `page: 1, 2, ...` until a page returns fewer results than `per_page`. A truncated list silently breaks name → ID resolution.
Applying resolved label IDs or a milestone ID to a specific issue or PR is out of scope here — that's `issue_write`/`pull_request_write` in the composing skill (`gitea-issues`/`gitea-prs`). If the task is a label operation, read `references/labels.md`; if a milestone operation, read `references/milestones.md`. If the label to apply has to be derived from conversation context rather than named, read `references/label-inference.md`.

View File

@@ -57,7 +57,11 @@ scope applied at once.
1. Read the conversation context (issue/PR title, body, or the triggering discussion) for the 1. Read the conversation context (issue/PR title, body, or the triggering discussion) for the
signals above. signals above.
2. Call `label_read method: "list_repo_labels"` (see `references/labels.md`) to get the current 2. Call `label_read method: "list_repo_labels"` (see `references/labels.md`) to get the current
label set with IDs — inference must never guess an ID, only a name, then resolve it. label set with IDs — inference must never guess an ID, only a name, then resolve it. Because
`exclusive` is an org-labels-only flag, this taxonomy plausibly lives at org scope too: for any
inferred name absent from the repo pool, also call `label_read method: "list_org_labels"` with
`org` set to the repo's `owner` before treating it as unresolved. A failure there means the owner
is a user account, not an organisation, so no org pool exists and the name is genuinely absent.
3. Match inferred label names against the resolved list (case-insensitive). If a scope group 3. Match inferred label names against the resolved list (case-insensitive). If a scope group
already has a different label applied on the target and a new one is inferred for that same already has a different label applied on the target and a new one is inferred for that same
scope, plan to replace rather than add (see above). scope, plan to replace rather than add (see above).

View File

@@ -62,9 +62,15 @@ label_read method: "get_repo_label" owner: <owner> repo: <repo> id: <id>
## Resolve a name to an ID ## Resolve a name to an ID
There is no direct name lookup. List all repo labels (paginating if needed), scan for a There is no direct name lookup. List all repo labels (paginating if needed), scan for a
case-insensitive name match, and extract `id`. This is the required first step before any label case-insensitive name match, and extract `id`. Both pools can apply to one issue: if the name is
application on an issue or PR — the actual `add_labels`/`replace_labels`/`remove_label` call lives not in `list_repo_labels`, also check `list_org_labels` before reporting it unresolved. That method
in `gitea-issues`/`gitea-prs` via `issue_write`/`pull_request_write`, which take numeric IDs only. takes `org`, not `owner`/`repo` — pass the repo's `owner` as `org`, which is what it means when the
owner is an organisation. If that call fails, the owner is a user account, there is no org pool, and
the miss is a real miss.
Resolution is the required first step before any label application on an issue or PR — the actual
`add_labels`/`replace_labels`/`remove_label` call lives in `gitea-issues`/`gitea-prs` via
`issue_write`/`pull_request_write`, which take numeric IDs only.
## Create a label ## Create a label

View File

@@ -4,7 +4,7 @@ List, read, create, update, merge, and review Gitea pull requests.
## What it does ## What it does
This skill handles the pull request lifecycle within the Gitea integration suite — listing and reading PRs (details, diff, changed files, CI status, reviews), creating them (title, body, labels), updating them (title, body, assignees, labels, milestone), managing reviewers, closing/reopening, merging with a chosen strategy and post-merge branch cleanup, and the full code-review flow (create a review with inline comments, submit it, dismiss or delete it). It composes `gitea-labels-milestones` for label/milestone ID resolution rather than duplicating that logic, and defers to `gitea-issues` for anything that turns out to be an issue rather than a PR (they share one number space) and to `gitea-branches`/`gitea-files` for the underlying branch/file operations behind a PR. This skill handles the pull request lifecycle within the Gitea integration suite — listing and reading PRs (details, diff, changed files, CI status, reviews), creating them (title, body, labels), updating them (title, body, assignees, labels, milestone), adding and removing reviewers, closing/reopening, merging with a chosen strategy and post-merge branch cleanup, and the full code-review flow (create a review with inline comments, submit it, dismiss or delete it, reply to a review comment, and resolve or unresolve a comment thread). It composes `gitea-labels-milestones` for label/milestone ID resolution rather than duplicating that logic — `milestone` applies on an update only, never on create — and defers to `gitea-issues` for anything that turns out to be an issue rather than a PR (they share one number space) and to `gitea-branches`/`gitea-files` for the underlying branch/file operations behind a PR.
## Usage ## Usage
@@ -18,8 +18,8 @@ Describe the PR or review task: list PRs, get a PR's status/diff/reviews, create
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `SKILL.md` | Skill instructions for agents — Gotchas, composition with `gitea-labels-milestones`, and the dispatch table | | `SKILL.md` | Skill instructions for agents — Gotchas, the dispatch table, and label/milestone ID resolution via `gitea-labels-milestones` |
| `references/pull-requests.md` | Execution detail for `list_pull_requests`, `pull_request_read` (get/get_diff/get_files/get_status), and `pull_request_write` (create/update/close/reopen/update_branch/add_reviewers/remove_reviewers) | | `references/pull-requests.md` | Execution detail for `list_pull_requests`, `pull_request_read` (get/get_diff/get_files/get_status), and `pull_request_write` (create/update/close/reopen/update_branch/add_reviewers/remove_reviewers) |
| `references/reviews.md` | Execution detail for `pull_request_review_write` (create/submit/delete/dismiss) and the review-related `pull_request_read` methods | | `references/reviews.md` | Execution detail for `pull_request_review_write` (create/submit/delete/dismiss, plus the comment-thread methods reply_comment/resolve_thread/unresolve_thread) and the review-related `pull_request_read` methods |
| `references/merging.md` | The merge workflow — CI vs. review/branch-protection gates, merge styles, branch cleanup, and the post-merge issue-close check | | `references/merging.md` | The merge workflow — CI vs. review/branch-protection gates, merge styles, branch cleanup, and the post-merge issue-close check |
| `references/sources.md` | Research sources backing the PR/review guidance | | `references/sources.md` | Research sources backing the PR/review guidance |

View File

@@ -2,14 +2,8 @@
name: gitea-prs name: gitea-prs
description: > description: >
Use when listing, reading, creating, updating, merging, or reviewing Gitea pull requests — Use when listing, reading, creating, updating, merging, or reviewing Gitea pull requests — even
getting PR status/diff/changed files/CI status, opening a PR, updating title/body/reviewers, when the user does not say "Gitea". Not issues -> `gitea-issues`.
closing/reopening, merging with a chosen strategy, or submitting/dismissing a code review with
inline comments. Composes `gitea-labels-milestones` to resolve label names or milestone titles
to the numeric IDs `pull_request_write` requires, rather than duplicating that resolution logic.
Do not use for issues (`gitea-issues`) or branch/commit operations (`gitea-branches`) — a number
the user mentions may refer to either an issue or a PR since they share one number space, so
confirm which domain applies before dispatching.
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.
@@ -20,49 +14,34 @@ metadata:
- gitea-mcp-slim-go - gitea-mcp-slim-go
- context7-websites-gitea - context7-websites-gitea
- context7-gitea-tea-cli - context7-gitea-tea-cli
version: "0.1.1" 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: mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp__gitea__pull_request_write mcp__gitea__pull_request_review_write
--- ---
## Gotchas ## Gotchas
- **Issues and PRs share one number space.** A number the user mentions (`#42`) might be an issue, not a PR — there is only one counter per repo. If you're not certain, call `pull_request_read method: "get"` and treat a 404 as "this number is an issue, not a PR" (or check `is_pull` on an `issue_read` response first if you already have one). - **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_read method: "get"` returns `review_scomments`, not `review_comments`.** Source-level typo in gitea-mcp v1.3.0. Never reference `review_comments` — it will always be undefined. - **`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.
- **`draft: true` on create prepends `"WIP:"` to the title.** Gitea has no first-class draft field — it implements draft PRs via title prefix. To un-draft, call `update` and pass the title without the `WIP:` prefix.
- **Cross-repo fork PRs require `head` as `"fork-owner:branch-name"`.** A bare branch name causes Gitea to search the base repo for it and return 422. Same-repo PRs use a bare branch name.
- **PR `milestone` is a bare title string, not `{id, title}`.** Unlike issues, you cannot recover a milestone's ID from a PR response. If you need the ID (e.g. to filter or to pass to another write), call into `gitea-labels-milestones` and match by title via `milestone_read method: "list"`.
- **CI status and review/approval state are independent merge gates.** `get_status` only reports CI. Branch-protection rules (required approvals, requested-reviewer coverage, stale-approval handling) are enforced server-side by the merge call itself and will error if unmet — passing CI does not mean the merge will succeed.
- **Reviews move through a state machine, not a single write.** `create` opens a review in `PENDING` state with inline comments attached; `submit` finalizes it with a terminal `state` (`APPROVED`/`REQUEST_CHANGES`/`COMMENT`). A submitted review can be `dismiss`ed afterward, but never deleted — `delete` only removes a review that was never submitted.
- **Merging a PR does not auto-close linked issues.** Unlike GitHub, Gitea has no merge-triggers-close event. It does parse closing keywords (`Fixes #N`, `Closes #N`) in commit messages landing on the default branch, so a non-squash merge that preserves those commit messages may auto-close the issue — but a squash merge rewrites history into one commit, so survival of the keyword depends on the squash commit's message. Always call `issue_read method: "get"` on any referenced issue after merging to check whether it already closed before deciding to close it explicitly.
## Composing `gitea-labels-milestones`
Before any `pull_request_write` call that includes a `labels` or `milestone` parameter, resolve names/titles to numeric IDs via `gitea-labels-milestones` — `label_read method: "list_repo_labels"` for label name → ID, `milestone_read method: "list"` for milestone title → ID. Never pass a label name string or milestone title string directly to `pull_request_write`; both parameters take numeric IDs only. This skill does not duplicate that lookup logic — it composes the shared skill.
## Dispatch ## Dispatch
| Task | Tool | method | Resolve `owner` and `repo` from context first, and confirm the number names a PR before writing to it.
|---|---|---|
| List PRs | `list_pull_requests` | — |
| Get PR details | `pull_request_read` | `"get"` |
| Get PR diff | `pull_request_read` | `"get_diff"` |
| Get PR changed files | `pull_request_read` | `"get_files"` |
| Get PR CI status | `pull_request_read` | `"get_status"` |
| Get PR reviews | `pull_request_read` | `"get_reviews"` |
| Get one review | `pull_request_read` | `"get_review"` |
| Get review inline comments | `pull_request_read` | `"get_review_comments"` |
| Create a PR | `pull_request_write` | `"create"` |
| Update a PR | `pull_request_write` | `"update"` |
| Close a PR | `pull_request_write` | `"close"` |
| Reopen a PR | `pull_request_write` | `"reopen"` |
| Merge a PR | `pull_request_write` | `"merge"` |
| Update branch from base | `pull_request_write` | `"update_branch"` |
| Add reviewers | `pull_request_write` | `"add_reviewers"` |
| Remove reviewers | `pull_request_write` | `"remove_reviewers"` |
| Create a review | `pull_request_review_write` | `"create"` |
| Submit a review | `pull_request_review_write` | `"submit"` |
| Delete a review | `pull_request_review_write` | `"delete"` |
| Dismiss a review | `pull_request_review_write` | `"dismiss"` |
For full parameter detail on listing/reading/creating/updating/closing PRs, read `references/pull-requests.md`. For review-specific detail (create/submit/delete/dismiss, inline comment shape), read `references/reviews.md`. For the merge workflow specifically (CI gate, merge styles, branch cleanup, post-merge issue check), read `references/merging.md`. | Task | Tool | Reference |
|---|---|---|
| List PRs; read a PR's details, diff, changed files or CI status | `list_pull_requests`, `pull_request_read` | `references/pull-requests.md` |
| Create a PR — subject to the silent-drop Gotcha above | `pull_request_write` | `references/pull-requests.md` |
| Update, close, reopen or retarget a PR, sync it with its base, or add/remove reviewers | `pull_request_write` | `references/pull-requests.md` |
| 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
`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.
Resolve a milestone only when the call is an `"update"` — on `"create"` the lookup is wasted, per the Gotcha above. Recovering an existing PR's milestone ID needs the same lookup, because `pull_request_read` returns `milestone` as a bare title string and never an ID.

View File

@@ -7,7 +7,7 @@ source_keys:
# Pull request read/write execution detail # Pull request read/write execution detail
Parameter signatures below are cross-checked live against the deployed gitea-mcp server tool schemas at authoring time — not copied verbatim from the plugin's research doc for this domain, which has a known history of drifting from the deployed server (e.g. a prior `type` parameter that no longer exists on `list_issues`, and the `review_scomments` typo covered in `references/reviews.md`). Re-verify via `ToolSearch` before trusting this file if the gitea-mcp version changes. Parameter signatures below are cross-checked live against the deployed gitea-mcp server tool schemas at authoring time — not copied verbatim from the plugin's research doc for this domain, which has a known history of drifting from the deployed server (e.g. a prior `type` parameter that no longer exists on `list_issues`). These files were last verified against gitea-mcp **v1.7.0**, as reported by `get_gitea_mcp_server_version`. Re-verify via `ToolSearch` before trusting this file if the deployed version differs — drift has bitten this skill in both directions, adding methods it does not list and fixing quirks it still warns about.
## `list_pull_requests` ## `list_pull_requests`
@@ -29,14 +29,14 @@ List responses trim PRs down to summary fields — `head`/`base` are bare ref st
- `owner` (string, required) - `owner` (string, required)
- `repo` (string, required) - `repo` (string, required)
- `pull_number` (number, required) - `pull_number` (number, required)
- `review_id` (number, optional) — required for `"get_review"` and `"get_review_comments"`; see `references/reviews.md` - `review_id` (number, optional) — required for `"get_review"`, which errors with `review_id is required` without it. **Optional** for `"get_review_comments"`: omit it to list every inline comment on the PR in one call. See `references/reviews.md`
- `binary` (boolean, optional) — include binary diff content for `"get_diff"` - `binary` (boolean, optional) — include binary diff content for `"get_diff"`
- `page` (number, optional, default 1) - `page` (number, optional, default 1)
- `per_page` (number, optional, default 30) - `per_page` (number, optional, default 30)
`"get"`, `"get_diff"`, `"get_files"`, and `"get_status"` are covered here. `"get_reviews"`, `"get_review"`, and `"get_review_comments"` are covered in `references/reviews.md`. `"get"`, `"get_diff"`, `"get_files"`, and `"get_status"` are covered here. `"get_reviews"`, `"get_review"`, and `"get_review_comments"` are covered in `references/reviews.md`.
- `"get"` returns the full PR object: state, draft, merged, mergeable flags; `head`/`base` as full objects (`{ref, sha, repo?}`); `milestone` as a bare title string (not `{id, title}`); `review_scomments` (typo, see `references/reviews.md`). - `"get"` returns the full PR object: state, draft, merged, mergeable flags; `head`/`base` as full objects (`{ref, sha, repo?}`); `milestone` as a bare title string (not `{id, title}`); and `review_comments` as an integer count, not comment objects (see `references/reviews.md`).
- `"get_diff"` returns raw diff text. - `"get_diff"` returns raw diff text.
- `"get_files"` returns the list of changed file objects. - `"get_files"` returns the list of changed file objects.
- `"get_status"` returns the combined commit status for the PR's head commit — CI result only, not review/approval state (see `references/merging.md`). - `"get_status"` returns the combined commit status for the PR's head commit — CI result only, not review/approval state (see `references/merging.md`).

View File

@@ -7,7 +7,7 @@ source_keys:
# PR review execution detail # PR review execution detail
Parameter signatures below are cross-checked live against the deployed gitea-mcp server tool schema, not copied from the plugin's research doc verbatim — same sourcing discipline as `references/pull-requests.md`. Parameter signatures below are cross-checked live against the deployed gitea-mcp server tool schema, not copied from the plugin's research doc verbatim — same sourcing discipline as `references/pull-requests.md`. Last verified against **v1.7.0**, as reported by `get_gitea_mcp_server_version`.
## Review state machine ## Review state machine
@@ -21,23 +21,34 @@ A review is not a single write. It moves through states:
## `pull_request_review_write` ## `pull_request_review_write`
**Parameters:** **Parameters:**
- `method` (string, required) — `"create"` | `"submit"` | `"delete"` | `"dismiss"` - `method` (string, required) — `"create"` | `"submit"` | `"delete"` | `"dismiss"` | `"reply_comment"` | `"resolve_thread"` | `"unresolve_thread"`
- `owner` (string, required) - `owner` (string, required)
- `repo` (string, required) - `repo` (string, required)
- `pull_number` (number, required) - `pull_number` (number, required for every method except `"resolve_thread"` and `"unresolve_thread"`, which locate the thread from `comment_id` alone) — the schema's own `required` list is only `method`/`owner`/`repo`, so a missing `pull_number` surfaces as a runtime error, not client-side validation
- `review_id` (number, required for every method except `"create"`, which returns the ID to use for the follow-up `submit`/`delete`/`dismiss` call) - `review_id` (number) — required for `"submit"`, `"delete"` and `"dismiss"`; `"create"` returns the ID to use for that follow-up call. Not used by `"reply_comment"`, `"resolve_thread"` or `"unresolve_thread"`, which key off `comment_id` instead
- `comment_id` (number, required for `"reply_comment"`, `"resolve_thread"` and `"unresolve_thread"`) — an individual review comment's ID, obtained from `pull_request_read method: "get_review_comments"`. For the two thread methods this must be the thread's **first** comment, not an arbitrary one in it
- `state` (string, optional) — `"APPROVED"` | `"REQUEST_CHANGES"` | `"COMMENT"` | `"PENDING"` — set on `"create"` (typically `"PENDING"`, or a terminal state to create-and-submit in one call if the server supports it) or `"submit"` (terminal state) - `state` (string, optional) — `"APPROVED"` | `"REQUEST_CHANGES"` | `"COMMENT"` | `"PENDING"` — set on `"create"` (typically `"PENDING"`, or a terminal state to create-and-submit in one call if the server supports it) or `"submit"` (terminal state)
- `body` (string, optional) — overall review comment text - `body` (string, optional) — the overall review comment text on `"create"`/`"submit"`; on `"reply_comment"` it is the reply text and is the payload of the call
- `commit_id` (string, optional, for `"create"`) — anchors inline comments to a specific commit SHA (typically the PR's current head SHA from `pull_request_read method: "get"`) - `commit_id` (string, optional, for `"create"`) — anchors inline comments to a specific commit SHA (typically the PR's current head SHA from `pull_request_read method: "get"`)
- `message` (string, optional, for `"dismiss"`) — dismissal reason - `message` (string, optional, for `"dismiss"`) — dismissal reason
- `comments` (array of objects, optional, for `"create"`) — inline comments, each: `{path, body, old_line_num, new_line_num}` — `path` is the file path, `body` is the comment text, `new_line_num` anchors to a line in the new (added) side of the diff, `old_line_num` anchors to a line in the old (removed) side; use whichever side the comment applies to, not both - `comments` (array of objects, optional, for `"create"`) — inline comments, each: `{path, body, old_line_num, new_line_num}` — `path` is the file path, `body` is the comment text, `new_line_num` anchors to a line in the new (added) side of the diff, `old_line_num` anchors to a line in the old (removed) side; use whichever side the comment applies to, not both
## Comment threads
Three further methods act on an individual review comment rather than on a review as a whole. They sit outside the state machine above — a thread can be replied to or resolved whatever state its parent review is in — and none of them takes a `review_id`.
- **`reply_comment`** — posts `body` as a reply to the comment named by `comment_id`, threading under it rather than starting a new top-level comment. Takes `pull_number`.
- **`resolve_thread`** — marks the thread containing `comment_id` resolved. Pass the thread's **first** comment ID; another ID in the same thread is not equivalent. Does not take `pull_number`.
- **`unresolve_thread`** — reopens a resolved thread, under the same first-comment rule.
Get the `comment_id` from `pull_request_read method: "get_review_comments"`. Call it with no `review_id` to list every inline comment on the PR, then pick the thread to act on; scoping it to one `review_id` only finds threads opened by that review.
## Reading reviews (`pull_request_read`) ## Reading reviews (`pull_request_read`)
- `method: "get_reviews"` — array of review summaries: `id`, `state`, `body`, `user` (login), `comments_count`, `submitted_at`, `html_url`, `stale` (bool — the PR was pushed to after this review was submitted, meaning it may be outdated), `official` (bool), `dismissed` (bool). - `method: "get_reviews"` — array of review summaries: `id`, `state`, `body`, `user` (login), `comments_count`, `submitted_at`, `html_url`, `stale` (bool — the PR was pushed to after this review was submitted, meaning it may be outdated), `official` (bool), `dismissed` (bool).
- `method: "get_review"` (requires `review_id`) — single review detail. - `method: "get_review"` (requires `review_id` — omitting it fails with `review_id is required`) — single review detail.
- `method: "get_review_comments"` (requires `review_id`) — array of inline comments: `id`, `body`, `path`, `position`, `old_position`, `diff_hunk`, `user`, `html_url`, `created_at`, `updated_at`. - `method: "get_review_comments"` (`review_id` **optional** — omit it to list every inline comment on the PR in one call, rather than one review's) — array of inline comments: `id`, `body`, `path`, `position`, `old_position`, `diff_hunk`, `user`, `html_url`, `created_at`, `updated_at`.
**`review_scomments` typo:** the full PR object returned by `pull_request_read method: "get"` includes a field named `review_scomments` (a count), not `review_comments` — a source-level misspelling in gitea-mcp v1.3.0's `slim.go`. Do not write code or instructions that reference `review_comments` on that response; it will always be `undefined`. This is distinct from the `get_review_comments` method above, which is spelled correctly and returns the actual comment objects. **`review_comments` on the `"get"` response is a count, not the comments.** The full PR object returned by `pull_request_read method: "get"` carries `review_comments` as an integer — the number of inline review comments. It is distinct from the `get_review_comments` method above, which returns the actual comment objects; reading the count is no substitute for that call. Older gitea-mcp releases misspelled this key as `review_scomments`; the misspelling was corrected upstream and the deployed v1.7.0 response carries no such key, so treat any instruction that reaches for `review_scomments` as stale.
**Inline-comment field names differ between write and read.** The `comments` array on `pull_request_review_write method: "create"` uses `old_line_num`/`new_line_num`. The `get_review_comments` read response uses different field names for the same concept — `position` (new-side line) and `old_position` (old-side line). Do not assume the same key names apply on both sides of the round trip. **Inline-comment field names differ between write and read.** The `comments` array on `pull_request_review_write method: "create"` uses `old_line_num`/`new_line_num`. The `get_review_comments` read response uses different field names for the same concept — `position` (new-side line) and `old_position` (old-side line). Do not assume the same key names apply on both sides of the round trip.

View File

@@ -3,7 +3,7 @@
## gitea-mcp-repo ## gitea-mcp-repo
- **URL:** https://gitea.com/gitea/gitea-mcp - **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. Live tool schemas (`list_pull_requests`, `pull_request_read`, `pull_request_write`, `pull_request_review_write`) were verified directly against the deployed MCP server via `ToolSearch` at authoring time, per this repo's process for resolving schema-vs-docs drift, rather than copied from the derived research doc. - **Description:** Official gitea-mcp repository — `operation/*.go` source files documenting the MCP tools, their parameters, and CLI flags. Live tool schemas (`list_pull_requests`, `pull_request_read`, `pull_request_write`, `pull_request_review_write`) are verified directly against the deployed MCP server via `ToolSearch`, per this repo's process for resolving schema-vs-docs drift, rather than copied from the derived research doc. **Last verified against v1.7.0**, as reported by `get_gitea_mcp_server_version`; the files were originally authored against v1.3.0 and the read/review side had drifted by three defects before that re-verification.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md, references/pull-requests.md, references/reviews.md, references/merging.md - **Contributing files:** SKILL.md, references/pull-requests.md, references/reviews.md, references/merging.md
- **Status:** `extracted` - **Status:** `extracted`
@@ -11,7 +11,7 @@
## gitea-mcp-slim-go ## gitea-mcp-slim-go
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/pull/slim.go - **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/pull/slim.go
- **Description:** Slim response shape structs from gitea-mcp source — defines exactly which fields the MCP server returns for PRs and reviews, including the `review_scomments` typo and the PR-response milestone-as-title-string quirk. - **Description:** Slim response shape structs from gitea-mcp source — defines exactly which fields the MCP server returns for PRs and reviews, including the PR-response milestone-as-title-string quirk. The `review_scomments` misspelling this file documented at v1.3.0 was corrected upstream; v1.7.0 returns `review_comments`.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md, references/pull-requests.md, references/reviews.md - **Contributing files:** SKILL.md, references/pull-requests.md, references/reviews.md
- **Status:** `extracted` - **Status:** `extracted`
@@ -21,7 +21,7 @@
- **URL:** context7:/websites/gitea - **URL:** context7:/websites/gitea
- **Description:** Official Gitea docs mirror on Context7 — branch protection rules, PR review/merge gating behavior, and automatic issue/PR cross-reference linking. Backfills the external/best-practice gap left by the original docs.gitea.com fetch timeout. - **Description:** Official Gitea docs mirror on Context7 — branch protection rules, PR review/merge gating behavior, and automatic issue/PR cross-reference linking. Backfills the external/best-practice gap left by the original docs.gitea.com fetch timeout.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md, references/merging.md - **Contributing files:** references/merging.md
- **Status:** `extracted` - **Status:** `extracted`
## context7-gitea-tea-cli ## context7-gitea-tea-cli

View File

@@ -2,12 +2,9 @@
name: gitea-releases name: gitea-releases
description: > description: >
Use when managing Gitea releases and tags for a repository: listing, creating, or deleting Use when managing Gitea releases or the git tags underneath them — list, get,
releases (with draft/prerelease flags and release notes), and listing, creating, or deleting the create, or delete either — even when the user does not say "release" or
underlying git tags. Use even if the user doesn't say "release" explicitly — "cut a v1.2.0", "Gitea". Not branches or commit history -> `gitea-branches`.
"publish a prerelease", "tag this commit", or "what's the latest release" all apply. Do not use
for git branch or commit history operations (use gitea-branches) or for issue/PR management (use
gitea-issues / gitea-prs).
metadata: metadata:
category: gitea category: gitea
@@ -20,11 +17,9 @@ metadata:
## Gotchas ## Gotchas
- **`delete_release` takes a numeric `id`, never a tag name.** `delete_tag` is the mirror opposite — it takes the `tag_name` string, never a numeric id. These two tools are asymmetric on purpose; passing a tag name to `delete_release` or a numeric id to `delete_tag` fails. Always resolve the numeric release id via `list_releases` or `get_release` first if you only have a tag name in hand. - **Deleting a release never deletes its tag, and deleting a tag never deletes the release wrapping it.** A release is a metadata wrapper around a tag, so removing both takes two independent destructive calls.
- **Deleting a release does not delete its tag.** They are separate destructive operations against separate resources — a release is a wrapper (title, notes, draft/prerelease flags, assets) around a tag, not the tag itself. If the intent is to remove both, call `delete_release` and `delete_tag` separately. - **`is_draft`/`is_pre_release` are booleans the caller sets — Gitea never infers a prerelease from a `-beta`/`-rc` tag name.** The response object names them `draft`/`prerelease`; passing `draft` as an input key is silently ignored, not rejected.
- **`list_releases`/`list_tags` default to `per_page: 20`**, unlike most other gitea-mcp tools which default to 30. The MCP layer does no auto-pagination — to get a complete result set, loop `page` upward until a page returns fewer than `per_page` results. - **`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.
- **`is_draft`/`is_pre_release` are explicit booleans the caller sets on `create_release` — never inferred from `tag_name`.** Note the input param is `is_draft`, which maps to the `draft` field on the *response* object (see Dispatch table below and `references/call-signatures.md`) — `draft` is never a valid input key. Practitioner convention (per the `tea` CLI) uses `-beta`/`-rc` suffixes for prereleases (e.g. `v2.0.0-beta.1`), but Gitea does not enforce or infer this from the tag string. If the user names a tag that looks like a prerelease, set `is_pre_release: true` explicitly rather than assuming the flag is redundant with the name.
- **Tag names are conventionally semver, `v`-prefixed** (`v1.2.0`, `v2.0.0-beta.1`), but this is a practitioner convention, not a Gitea constraint — don't reject or rewrite a caller-supplied tag name that doesn't follow it.
## Dispatch table ## Dispatch table
@@ -40,13 +35,13 @@ metadata:
| Create tag | `create_tag` | `owner`, `repo`, `tag_name` | `target`, `message` | | Create tag | `create_tag` | `owner`, `repo`, `tag_name` | `target`, `message` |
| Delete tag | `delete_tag` | `owner`, `repo`, `tag_name` | — | | Delete tag | `delete_tag` | `owner`, `repo`, `tag_name` | — |
`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. See `references/call-signatures.md` for response shapes. `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.
## Workflow ## Workflow
- [ ] **Creating a release:** Call `create_release` directly with `tag_name` + `target` + `title` — Gitea is assumed to create the underlying tag automatically if `tag_name` doesn't already exist (this is plausible behavior inferred from the API shape, not directly confirmed in the research docs), so a separate `create_tag` call is only needed when you want to tag a commit without wrapping it in a release yet. Verify the tag exists afterward if this matters to the caller. Set `is_pre_release`/`is_draft` explicitly per the Gotchas above; don't leave them to default inference. - [ ] **Creating a release:** Call `create_release` with `tag_name`, `target`, and `title`. 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 safely:** Resolve the numeric id first — call `list_releases` (paginate if needed, see Gotchas) or `get_release` if the id is already known, find the entry matching the target `tag_name`, then call `delete_release` with that `id`. Never pass `tag_name` to `delete_release`. - [ ] **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 (frees the id lookup), then call `delete_tag` with the `tag_name` separately — confirm both are intended before proceeding, since each is an independent irreversible operation. - [ ] **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:** If the caller needs all releases or tags (not just the first page), loop `page: 1, 2, 3...` until a response has fewer than `per_page` entries. - [ ] **Listing every page:** Loop `page: 1, 2, 3...` until a response returns fewer than `per_page` entries. Nothing here auto-paginates.
If exact response field shapes or additional conventions are needed, read `references/call-signatures.md` and `references/conventions.md`. If exact input params or response field shapes are needed, read `references/call-signatures.md`. If the caller raises semver tag naming, release-notes sourcing, or how a release relates to its tag, read `references/conventions.md`.

View File

@@ -42,7 +42,6 @@
- **Research doc:** plugins/gitea/docs/research/docs/gitea/workflow-conventions.md (Release and tag conventions section) - **Research doc:** plugins/gitea/docs/research/docs/gitea/workflow-conventions.md (Release and tag conventions section)
**Contributing files:** **Contributing files:**
- SKILL.md (Gotchas — semver tag naming) - references/conventions.md (semver tag naming, release-notes sourcing)
- references/conventions.md
**Status:** `extracted` **Status:** `extracted`

View File

@@ -4,7 +4,7 @@ Human-facing entry point and router for the Gitea integration.
## What it does ## What it does
This skill is the conversational front door to the Gitea suite — it replaces the old flat `/gitea` skill. On its own it never calls a Gitea MCP tool; it composes the six domain skills (`gitea-issues`, `gitea-labels-milestones`, `gitea-prs`, `gitea-branches`, `gitea-files`, `gitea-releases`). It handles the no-args status check-in (open issues + open PRs), resolves ambiguous issue-or-PR numbers before dispatching (issues and PRs share one number space), and points a user or agent to the right domain skill when it's unclear which one applies. This skill is the conversational front door to the Gitea suite — it replaces the old flat `/gitea` skill. On its own it never calls a Gitea MCP tool; it composes the six domain skills (`gitea-issues`, `gitea-labels-milestones`, `gitea-prs`, `gitea-branches`, `gitea-files`, `gitea-releases`). It handles the no-args status check-in (open issues + open PRs), which preserves the original flat `/gitea` skill's default behavior; resolves ambiguous issue-or-PR numbers before dispatching (issues and PRs share one number space); and points a user or agent to the right domain skill when it's unclear which one applies.
## Usage ## Usage
@@ -18,5 +18,8 @@ Invoke with no arguments for a status check-in, with a bare number to resolve an
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `SKILL.md` | Skill instructions for agents — Gotchas, status view, ambiguous-number resolution, and the domain-skill index | | `SKILL.md` | Skill instructions for agents — Gotchas, the dispatch table keyed on invocation shape, and the common report gate every branch ends in — each branch's own format lives with its reference file |
| `references/status-checkin.md` | Loaded when the skill is invoked with no specific request — the two parallel open-issue/open-PR reads and the two-section report |
| `references/number-resolution.md` | Loaded when the request carries a bare number that says neither "issue" nor "PR" — the `is_pull` resolution call and the hidden-permission-error 404 |
| `references/skill-index.md` | Loaded when the request names a capability but not which skill owns it — the six-skill routing index |
| `references/sources.md` | Research sources backing the routing/status guidance | | `references/sources.md` | Research sources backing the routing/status guidance |

View File

@@ -2,25 +2,17 @@
name: gitea-workflow name: gitea-workflow
description: > description: >
Use when a human wants a general or ambiguous Gitea status check or isn't sure which Gitea Use when a Gitea request is general or ambiguous — a no-args repo check-in, a bare number that
domain skill applies — a no-args check-in ("what's going on in the repo", "any updates?"), could be an issue or a PR, or a capability whose owning skill is unclear. Resolves which
a bare-numbered reference that could be an issue or a PR ("what's the status of #42", "what's domain skill applies. Not an unambiguous issue request -> `gitea-issues`. Not an
happening with #17"), or a request to discover which Gitea capability handles a task. This is unambiguous PR request -> `gitea-prs`. Not local git work with no Gitea component.
the human-facing entry point and router for the Gitea integration — it replaces the old flat
`/gitea` invocation (now `/gitea-workflow`) and composes the six domain skills
(`gitea-issues`, `gitea-labels-milestones`, `gitea-prs`, `gitea-branches`, `gitea-files`,
`gitea-releases`) rather than calling any Gitea MCP tool directly. Do not use this skill when
the domain is already known and unambiguous — invoke the matching domain skill directly instead
(e.g. "create an issue" → `gitea-issues`, "merge PR #10" → `gitea-prs`, "cut a release" →
`gitea-releases`). Do not use for local git operations with no Gitea component (use
`git-workflow`).
compatibility: Requires Gitea MCP server configured with a token; delegates all calls to the six compatibility: Requires Gitea MCP server configured with a token; delegates all calls to the six
domain skills, which in turn require write:issue and write:repository scopes at minimum. domain skills, which in turn require write:issue and write:repository scopes at minimum.
metadata: metadata:
category: integration category: integration
version: "0.1.0" version: "0.1.3"
source_keys: source_keys:
- gitea-mcp-repo - gitea-mcp-repo
- gitea-mcp-slim-go - gitea-mcp-slim-go
@@ -29,46 +21,24 @@ metadata:
## Gotchas ## Gotchas
- **This skill never calls a Gitea MCP tool itself.** Every read or write goes through one of the six domain skills. If a request needs a raw `mcp__gitea__*` call that no domain skill exposes, that's a gap in a domain skill, not something to patch here. - **This skill never calls a Gitea MCP tool itself.** Every read and write goes through a domain skill. A needed `mcp__gitea__*` call that no domain skill exposes is a gap in that skill, not something to patch here.
- **Issues and PRs share one number space** — a bare number like `#42` could be either. Never guess from context clues alone; resolve it with a real call (see Step 2) before dispatching.
- **A 404 on the resolution call doesn't necessarily mean the number doesn't exist.** Gitea hides permission errors as not-found (documented in `gitea-issues`' Gotchas). If resolution 404s unexpectedly, say so and suggest checking token scope rather than reporting "no such issue or PR."
## Step 1 — Default status view (no args) ## Dispatch
When invoked with no specific request, give a status check-in: The invocation's shape selects exactly one branch.
1. Invoke `gitea-issues` to list open issues (`state: "open"`). | Invocation shape | Flow | Reference |
2. Invoke `gitea-prs` to list open PRs (`state: "open"`). |---|---|---|
3. Run both in parallel — they're independent reads. | No arguments, no specific request | Repo status check-in | `references/status-checkin.md` |
4. Report as two sections, "Open Issues" and "Open Pull Requests", each as a compact list (number, title). This preserves the original flat `/gitea` skill's default behavior. | A bare number, with neither "issue" nor "PR" said | Resolve which domain the number belongs to | `references/number-resolution.md` |
| A named capability whose owning skill is unclear | Route to the domain skill that owns it | `references/skill-index.md` |
## Step 2 — Resolve an ambiguous number If the invocation carries no specific request, read `references/status-checkin.md`.
When the user references a bare number without saying "issue" or "PR" (e.g. "what's going on with #42"): If the request references a bare number and never says "issue" or "PR", read `references/number-resolution.md`.
1. Invoke `gitea-issues` to run `issue_read method: "get"` on that number. If the request names a capability but not which skill owns it, read `references/skill-index.md`.
2. Check the response's `is_pull` field:
- `true` → it's a PR. Invoke `gitea-prs` for full PR detail (status, diff, reviews as appropriate to the request) and present that instead.
- `false` or absent → it's an issue. Present the issue detail already retrieved.
3. If the resolution call 404s, don't conclude the number doesn't exist — report the 404 and suggest verifying token scope (`write:issue`) per `gitea-issues`' Gotchas, since permission errors are hidden as not-found in Gitea.
Never dispatch to `gitea-issues` or `gitea-prs` based on guessing from phrasing alone ("that sounds like a bug" is not evidence) — always resolve first. ## Report
## Step 3 — Route explicit but domain-unclear requests Every branch ends here. Present results in plain language; the branch's reference file carries its format.
For requests that name a capability but not obviously which skill owns it, use this index:
| Skill | Covers |
|---|---|
| `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-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. |
If a request clearly names one of these (e.g. "create a milestone" → `gitea-labels-milestones`, "read this file from the repo" → `gitea-files`), invoke that skill directly rather than routing through here. Use this table only when the user or an upstream agent is unsure which skill applies.
## Step 4 — Report
Present results in plain language. For the status view, two labeled sections. For a resolved ambiguous number, say which domain it turned out to be before showing detail ("That's a pull request:" / "That's an issue:"). For routing, name the skill and hand off — don't duplicate its output format, let it report.

View File

@@ -0,0 +1,19 @@
---
topic: number-resolution
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
- context7-websites-gitea
---
# Resolving a bare issue-or-PR number
The user has referenced a bare number without saying "issue" or "PR" (e.g. "what's going on with #42"). Resolve it with a real call — never dispatch to `gitea-issues` or `gitea-prs` by guessing from phrasing alone, because "that sounds like a bug" is not evidence and the two domains share one number space.
1. Invoke `gitea-issues` to run `issue_read method: "get"` on that number.
2. Check the response's `is_pull` field:
- `true` → it's a PR. Invoke `gitea-prs` for full PR detail (status, diff, reviews as appropriate to the request) and present that instead.
- `false` or absent → it's an issue. Present the issue detail already retrieved.
3. If the resolution call 404s, don't conclude the number doesn't exist. Gitea hides permission errors as not-found (documented in `gitea-issues`' Gotchas), so report the 404 and suggest verifying the token carries `write:issue` rather than reporting "no such issue or PR."
Then report per `SKILL.md`'s Report section, saying which domain the number turned out to be before showing detail ("That's a pull request:" / "That's an issue:") — otherwise the user cannot tell the resolution happened.

View File

@@ -0,0 +1,22 @@
---
topic: skill-index
source_keys:
- gitea-mcp-repo
---
# Domain-skill index
The request names a capability but not obviously which skill owns it. Find the owner here, then invoke it:
| Skill | Covers |
|---|---|
| `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-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. |
A request that already names its own owner (e.g. "create a milestone" → `gitea-labels-milestones`, "read this file from the repo" → `gitea-files`) never needed this index — invoke that skill directly rather than routing through here.
Then report per `SKILL.md`'s Report section: name the skill and hand off — don't duplicate its output format, let it report.

View File

@@ -3,9 +3,9 @@
## gitea-mcp-repo ## gitea-mcp-repo
- **URL:** https://gitea.com/gitea/gitea-mcp - **URL:** https://gitea.com/gitea/gitea-mcp
- **Description:** Official gitea-mcp repository (v1.3.0) — `operation/*.go` source files documenting all 55 MCP tools. This skill's status view relies on `list_issues`/`list_pull_requests` semantics (via `gitea-issues`/`gitea-prs`), and its ambiguous-number resolution relies on `issue_read`'s `is_pull` field, both verified against this source at authoring time. - **Description:** Official gitea-mcp repository (v1.3.0) — `operation/*.go` source files documenting all 55 MCP tools. This skill's status view relies on `list_issues`/`list_pull_requests` semantics (via `gitea-issues`/`gitea-prs`), its ambiguous-number resolution relies on `issue_read`'s `is_pull` field, and its domain-skill index groups that tool surface by owning skill — all verified against this source at authoring time.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md - **Contributing files:** references/status-checkin.md, references/number-resolution.md, references/skill-index.md
- **Status:** `extracted` - **Status:** `extracted`
## gitea-mcp-slim-go ## gitea-mcp-slim-go
@@ -13,7 +13,7 @@
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/issue/slim.go - **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/issue/slim.go
- **Description:** Slim response shape structs from gitea-mcp source — confirms `is_pull` is present on a single-item `issue_read` response, the field this skill's resolution step depends on to distinguish an issue from a PR sharing the same number. - **Description:** Slim response shape structs from gitea-mcp source — confirms `is_pull` is present on a single-item `issue_read` response, the field this skill's resolution step depends on to distinguish an issue from a PR sharing the same number.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md - **Contributing files:** references/number-resolution.md
- **Status:** `extracted` - **Status:** `extracted`
## context7-websites-gitea ## context7-websites-gitea
@@ -21,7 +21,7 @@
- **URL:** context7:/websites/gitea - **URL:** context7:/websites/gitea
- **Description:** Official Gitea docs mirror on Context7 — confirms issues and pull requests share a single per-repository number sequence, and that Gitea returns 404 for permission failures rather than a distinct 403, both facts this skill's resolution and error-handling steps depend on. - **Description:** Official Gitea docs mirror on Context7 — confirms issues and pull requests share a single per-repository number sequence, and that Gitea returns 404 for permission failures rather than a distinct 403, both facts this skill's resolution and error-handling steps depend on.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md - **Contributing files:** references/number-resolution.md
- **Status:** `extracted` - **Status:** `extracted`
## context7-gitea-tea-cli ## context7-gitea-tea-cli

View File

@@ -0,0 +1,15 @@
---
topic: status-checkin
source_keys:
- gitea-mcp-repo
---
# Status check-in (invoked with no request)
Give a repo status check-in:
1. Invoke `gitea-issues` to list open issues (`state: "open"`).
2. Invoke `gitea-prs` to list open PRs (`state: "open"`). Run this alongside step 1 — the two are independent reads, so serialising them only adds latency.
3. Report as two sections, "Open Issues" and "Open Pull Requests", each a compact list of number and title.
Then report per `SKILL.md`'s Report section.