feat(kyberforge): add gitea dispatch skill with research docs

Adds the gitea skill to kyberforge — a dispatch skill for managing
Defame1297/holocron via Gitea MCP from within Claude Code. Covers
issues, PRs, milestones, labels, branches, and status. Owner/repo
derived from git remote at runtime; no config required.

Also adds research docs (api-reference, data-model, examples,
overview, troubleshooting) and token-access reference used during
authoring and available for runtime scope lookups.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 18:42:29 +00:00
parent 04e7cfa089
commit b9c73cc0b2
9 changed files with 1429 additions and 0 deletions

View File

@@ -0,0 +1,693 @@
---
topic: api-reference
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Gitea MCP — Tool Reference
Full parameter schemas and response shapes for all MCP tools. Tools marked `[W]` are write operations suppressed in `--read-only` mode. Parameters listed as `required*` are conditionally required depending on the `method` value.
---
## Issues
### `list_issues` [R]
List repository issues or pull requests.
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `state` (string, optional, default: `"all"`) — `"open"`, `"closed"`, `"all"`
- `type` (string, optional) — `"issues"` or `"pulls"` to filter; omit for both
- `labels` (array of strings, optional) — filter by label names
- `milestones` (array, optional) — filter by milestone name or ID
- `since` (string, optional) — ISO 8601; issues updated after this time
- `before` (string, optional) — ISO 8601; issues updated before this time
- `page` (number, optional, default: 1)
- `per_page` (number, optional, default: 30)
**Response shape (list item):**
```
number, title, state, html_url, user (login), comments, created_at, updated_at
labels? ([]string), milestone? ({id, title}), ref?, deadline?
```
Body and `closed_at` are omitted from list responses.
---
### `issue_read` [R]
Read a single issue's details, comments, or label list.
**Parameters:**
- `method` (string, required) — `"get"` | `"get_comments"` | `"get_labels"`
- `owner` (string, required)
- `repo` (string, required)
- `issue_number` (number, required)
**Response shapes:**
`get` — full issue:
```
number, title, body, state, html_url, user, labels ([]string),
comments, created_at, updated_at, closed_at
assignees? ([]string), milestone? ({id, title}), ref?, deadline?,
is_pull? (true — present only when backed by a PR)
```
`get_comments` — array of:
```
id (int64), body, user, html_url, created_at, updated_at
```
`get_labels` — array of label objects from the Gitea API (not slimmed, full label objects including id, name, color).
---
### `issue_write` [W]
Create or mutate an issue, its comments, or its labels.
**Parameters:**
- `method` (string, required) — one of:
`"create"` | `"update"` | `"add_comment"` | `"edit_comment"` | `"add_labels"` | `"remove_label"` | `"replace_labels"` | `"clear_labels"`
- `owner` (string, required)
- `repo` (string, required)
- `issue_number` (number, required for all except `"create"`)
- `title` (string, required for `"create"`)
- `body` (string, required for `"create"`, `"add_comment"`, `"edit_comment"`)
- `assignees` (array of strings, optional) — login names
- `milestone` (number, optional) — milestone ID (not title, not number)
- `state` (string, optional) — `"open"` | `"closed"` | `"all"` (for `"update"`)
- `commentID` (number, optional, required for `"edit_comment"`)
- `labels` (array of numbers, optional) — label IDs (not names) for add/replace ops
- `label_id` (number, optional, required for `"remove_label"`)
- `ref` (string, optional) — branch association
- `deadline` (string, optional) — ISO 8601
- `remove_deadline` (boolean, optional)
**Critical:** `labels` takes IDs (numbers), not names. Must resolve label names to IDs via `label_read` before labeling.
---
### `search_issues` [R]
Search issues and PRs across repositories.
**Parameters:**
- `query` (string, required)
- `state` (string, optional) — `"open"` | `"closed"` | `"all"`
- `type` (string, optional) — `"issues"` | `"pulls"`
- `labels` (string, optional) — comma-separated label names
- `owner` (string, optional) — restrict to owner
- `page` (number, optional, default: 1)
- `per_page` (number, optional, default: 30)
---
## Pull Requests
### `list_pull_requests` [R]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `state` (string, optional, default: `"all"`) — `"open"` | `"closed"` | `"all"`
- `sort` (string, optional, default: `"recentupdate"`) — `"oldest"` | `"recentupdate"` | `"leastupdate"` | `"mostcomment"` | `"leastcomment"` | `"priority"`
- `milestone` (number, optional) — milestone ID filter
- `page` (number, optional, default: 1)
- `per_page` (number, optional, default: 30)
**Response shape (list item — heavily trimmed):**
```
number, title, state, draft, merged, html_url, user, created_at, updated_at
head? (ref string only), base? (ref string only), labels? ([]string)
```
Body, mergeable, comments, closed_at, and diff stats are omitted from list responses. Head/base are bare ref strings in the list, not objects.
---
### `pull_request_read` [R]
**Parameters:**
- `method` (string, required) — `"get"` | `"get_diff"` | `"get_files"` | `"get_status"` | `"get_reviews"` | `"get_review"` | `"get_review_comments"`
- `owner` (string, required)
- `repo` (string, required)
- `pull_number` (number, required)
- `review_id` (number, optional, required for `"get_review"` and `"get_review_comments"`)
- `binary` (boolean, optional) — include binary diff
- `page` (number, optional, default: 1)
- `per_page` (number, optional, default: 30)
**Response shapes:**
`get` — full PR:
```
number, title, body, state, draft, merged, mergeable, html_url, user,
labels ([]string), comments, created_at, updated_at, closed_at
head? ({ref, sha, repo?: {full_name, description}}),
base? ({ref, sha, repo?: {full_name, description}}),
additions?, deletions?, changed_files?,
merged_at?, merge_commit_sha?, merged_by?,
assignees? ([]string),
milestone? (string — title only, NOT an object),
review_scomments? (int — note: misspelled key in source, not review_comments)
```
`get_diff` — raw diff text
`get_files` — list of changed file objects
`get_status` — combined commit status for the PR head commit
`get_reviews` — array of review objects:
```
id, state, body, user (login), comments_count, submitted_at, html_url,
stale (bool), official (bool), dismissed (bool)
```
`get_review_comments` — array of inline review comments:
```
id, body, path, position (new line), old_position, diff_hunk,
user (login), html_url, created_at, updated_at
```
---
### `pull_request_write` [W]
**Parameters:**
- `method` (string, required) — `"create"` | `"update"` | `"close"` | `"reopen"` | `"merge"` | `"update_branch"` | `"add_reviewers"` | `"remove_reviewers"`
- `owner` (string, required)
- `repo` (string, required)
- `pull_number` (number, required except for `"create"`)
- `title` (string, required for `"create"`, optional for `"update"` and `"merge"`)
- `body` (string, required for `"create"`, optional for `"update"`)
- `head` (string, required for `"create"`) — source branch; use `owner:branch` for cross-repo
- `base` (string, required for `"create"`) — target branch
- `assignee` (string, optional)
- `assignees` (array of strings, optional)
- `milestone` (number, optional) — milestone ID
- `state` (string, optional) — `"open"` | `"closed"`
- `allow_maintainer_edit` (boolean, optional)
- `labels` (array of numbers, optional) — label IDs
- `deadline` (string, optional) — ISO 8601
- `remove_deadline` (boolean, optional)
- `merge_style` (string, optional, default: `"merge"`) — `"merge"` | `"rebase"` | `"rebase-merge"` | `"squash"` | `"fast-forward-only"`
- `message` (string, optional) — merge commit message or review dismissal reason
- `delete_branch` (boolean, optional) — delete head branch after merge
- `force_merge` (boolean, optional) — merge even if checks fail
- `merge_when_checks_succeed` (boolean, optional)
- `head_commit_id` (string, optional) — expected head SHA for conflict detection
- `reviewers` (array of strings, optional) — login names
- `team_reviewers` (array of strings, optional)
- `draft` (boolean, optional) — marks PR as draft by prefixing title with `"WIP:"`
---
### `pull_request_review_write` [W]
**Parameters:**
- `method` (string, required) — `"create"` | `"submit"` | `"delete"` | `"dismiss"`
- `owner` (string, required)
- `repo` (string, required)
- `pull_number` (number, required)
- `review_id` (number, optional, required except for `"create"`)
- `state` (string, optional) — `"APPROVED"` | `"REQUEST_CHANGES"` | `"COMMENT"` | `"PENDING"`
- `body` (string, optional)
- `commit_id` (string, optional) — for `"create"`; anchors inline comments to a commit
- `message` (string, optional) — dismissal reason for `"dismiss"`
- `comments` (array, optional) — inline comments for `"create"`:
each object: `{path, body, old_line_num, new_line_num}`
---
## Labels
### `label_read` [R]
**Parameters:**
- `method` (string, required) — `"list_repo_labels"` | `"get_repo_label"` | `"list_org_labels"`
- `owner` (string, optional, required for repo methods)
- `repo` (string, optional, required for repo methods)
- `org` (string, optional, required for org methods)
- `id` (number, optional, required for `"get_repo_label"`)
- `page` (number, optional, default: 1)
- `per_page` (number, optional, default: 30)
**Response:** Full label objects including `id`, `name`, `color`, `description`, `exclusive`, `is_archived`. Use this to map label names to IDs before write operations.
---
### `label_write` [W]
**Parameters:**
- `method` (string, required) — `"create_repo_label"` | `"edit_repo_label"` | `"delete_repo_label"` | `"create_org_label"` | `"edit_org_label"` | `"delete_org_label"`
- `owner` (string, optional, required for repo methods)
- `repo` (string, optional, required for repo methods)
- `org` (string, optional, required for org methods)
- `id` (number, optional, required for edit/delete)
- `name` (string, optional, required for create)
- `color` (string, optional, required for create) — hex format `#RRGGBB`
- `description` (string, optional)
- `exclusive` (boolean, optional) — org labels only; makes label mutually exclusive within a group
- `is_archived` (boolean, optional) — repo labels only
---
## Milestones
### `milestone_read` [R]
**Parameters:**
- `method` (string, required) — `"get"` | `"list"`
- `owner` (string, required)
- `repo` (string, required)
- `id` (number, optional, required for `"get"`) — milestone ID
- `state` (string, optional, default: `"all"`) — `"open"` | `"closed"` | `"all"`
- `name` (string, optional) — filter by title for `"list"`
- `page` (number, optional, default: 1)
- `per_page` (number, optional, default: 30)
**Response:** Full milestone objects including `id`, `title`, `description`, `state`, `due_on`, `closed_at`, `open_issues`, `closed_issues`.
---
### `milestone_write` [W]
**Parameters:**
- `method` (string, required) — `"create"` | `"update"` | `"edit"` | `"delete"`
(`"update"` and `"edit"` are aliases for the same operation)
- `owner` (string, required)
- `repo` (string, required)
- `id` (number, optional, required for update/delete)
- `title` (string, optional, required for create)
- `description` (string, optional)
- `due_on` (string, optional) — ISO 8601 date
- `state` (string, optional) — `"open"` | `"closed"`
---
## Branches
### `list_branches` [R]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `page` (number, optional, default: 1)
- `per_page` (number, optional, default: 30)
**Response shape per branch:**
```
name (string), protected (bool), commit_sha? (string — present when Commit != nil)
```
---
### `create_branch` [W]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `branch` (string, required) — new branch name
- `old_branch` (string, optional) — source branch; defaults to repo default branch
---
### `delete_branch` [W]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `branch` (string, required)
---
## Commits
### `list_commits` [R]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `sha` (string, optional) — starting SHA or branch name
- `path` (string, optional) — filter commits touching this path
- `page` (number, optional, default: 1, min: 1)
- `per_page` (number, optional, default: 30, min: 1)
**Response shape per commit:**
```
sha, html_url, created
message? (string — present when RepoCommit != nil),
author? ({name, email, date} — present when RepoCommit.Author != nil)
```
---
### `get_commit` [R]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `sha` (string, required)
**Response:** Same shape as list commit but always includes full detail.
---
## Files
### `get_file_contents` [R]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `ref` (string, required) — branch name, tag, or commit SHA
- `path` (string, required)
- `withLines` (boolean, optional) — return content with line numbers
**Response:**
```
name, path, sha, type, size
content? (string, base64-encoded), encoding?, html_url?, download_url?
```
The `sha` in the response is required when updating or deleting this file.
---
### `create_or_update_file` [W]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `path` (string, required)
- `content` (string, required) — base64-encoded file content
- `message` (string, required) — commit message
- `branch_name` (string, required) — target branch
- `sha` (string, optional, required when updating an existing file) — current file SHA
- `new_branch_name` (string, optional) — create a new branch during the operation
To update an existing file: must provide the file's current `sha` (get it from `get_file_contents` first). Without `sha`, Gitea treats the operation as a create and returns 409 if the file already exists.
---
### `delete_file` [W]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `path` (string, required)
- `message` (string, required) — commit message
- `branch_name` (string, required)
- `sha` (string, required) — current file SHA; must match the server's current SHA
---
### `get_dir_contents` [R]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `ref` (string, required)
- `path` (string, required)
**Response:** Array of directory entry objects:
```
name, path, type, size
```
No sha, no content, no URLs in directory listings.
---
### `get_repository_tree` [R]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `tree_sha` (string, required) — SHA, branch, or tag
- `recursive` (boolean, optional) — recurse into subdirectories
- `page` (number, optional, default: 1)
- `per_page` (number, optional, default: 30)
**Response:**
```
{sha, truncated (bool), total_count (int), tree: [{path, mode, type, size, sha}, ...]}
```
---
## Releases and Tags
### `list_releases` [R]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `is_draft` (boolean, optional)
- `is_pre_release` (boolean, optional)
- `page` (number, optional, default: 1, min: 1)
- `per_page` (number, optional, default: 20, min: 1)
**Response shape per release:**
```
id, tag_name, target (commitish), title, body (from Note field),
draft, prerelease, html_url, author (login), created_at, published_at
```
---
### `create_release` [W]
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `tag_name` (string, required)
- `target` (string, required) — branch, tag, or commit SHA
- `title` (string, required)
- `is_draft` (boolean, optional)
- `is_pre_release` (boolean, optional)
- `body` (string, optional) — release notes
---
### `get_release` / `get_latest_release` [R]
`get_release` requires `id` (number). `get_latest_release` takes only `owner` and `repo`.
---
### `delete_release` [W]
- `owner`, `repo`, `id` (number) — release numeric ID from list/get response.
---
### Tag tools
**`list_tags`** — `owner`, `repo`, `page`, `per_page` (default 20). Response per tag: `name`, `commit_sha?`. Message is dropped from list responses.
**`get_tag`** — `owner`, `repo`, `tag_name`. Full response: `name`, `message`, `commit_sha?`.
**`create_tag`** [W] — `owner`, `repo`, `tag_name` (required), `target` (commitish, optional), `message` (optional).
**`delete_tag`** [W] — `owner`, `repo`, `tag_name`.
---
## Search
### `search_repos` [R]
- `query` (string, required)
- `keywordIsTopic` (boolean, optional)
- `keywordInDescription` (boolean, optional)
- `ownerID` (number, optional) — filter by numeric user/org ID
- `isPrivate` (boolean, optional)
- `isArchived` (boolean, optional)
- `sort`, `order` (string, optional)
- `page`, `per_page` (default 30)
`search_repos` is the workaround for listing a user's repos when `read:user` scope is unavailable. Requires knowing the numeric `ownerID`.
---
### `search_users` [R]
- `query` (string, required)
- `page`, `per_page` (default 30)
---
### `search_org_teams` [R]
- `org` (string, required)
- `query` (string, required)
- `includeDescription` (boolean, optional)
- `page`, `per_page` (default 30)
---
## Repository Management
### `create_repo` [W]
- `name` (string, required)
- `description`, `private`, `issue_labels`, `auto_init`, `template`, `gitignores`, `license`, `readme`, `default_branch`, `trust_model`, `object_format_name` (all optional)
- `organization` (string, optional) — creates under org; defaults to personal account
- `trust_model` — `"default"` | `"collaborator"` | `"committer"` | `"collaboratorcommitter"`
- `object_format_name` — `"sha1"` | `"sha256"`
### `fork_repo` [W]
- `user` (string, required) — source repo owner
- `repo` (string, required) — source repo name
- `organization` (string, optional) — target org
- `name` (string, optional) — fork name
### `list_my_repos` [R]
Requires `read:user` scope. Without it, use `search_repos`.
- `page`, `per_page` (default 30, min 1)
### `list_org_repos` [R]
- `org` (string, required)
- `page`, `per_page` (default 100, min 1)
---
## User
### `get_me` [R]
No parameters. Returns current authenticated user. Requires `read:user` scope.
### `get_user_orgs` [R]
- `page`, `per_page` (default 30)
Requires `read:user` scope.
---
## Actions (CI)
### `actions_config_read` [R]
- `method` — `"list_repo_secrets"` | `"list_org_secrets"` | `"list_repo_variables"` | `"get_repo_variable"` | `"list_org_variables"` | `"get_org_variable"`
- `owner`, `repo` (for repo methods), `org` (for org methods)
- `name` (for get methods)
- `page`, `per_page` (default 30, min 1)
### `actions_config_write` [W]
- `method` — `"upsert_repo_secret"` | `"delete_repo_secret"` | `"upsert_org_secret"` | `"delete_org_secret"` | `"create_repo_variable"` | `"update_repo_variable"` | `"delete_repo_variable"` | `"create_org_variable"` | `"update_org_variable"` | `"delete_org_variable"`
- `name` (secret/variable name), `data` (secret value), `value` (variable value), `description` (optional)
### `actions_run_read` [R]
- `method` — `"list_workflows"` | `"get_workflow"` | `"list_runs"` | `"get_run"` | `"list_jobs"` | `"list_run_jobs"` | `"get_job_log_preview"` | `"download_job_log"`
- `owner`, `repo` (required)
- `workflow_id` (string) — workflow ID or filename
- `run_id`, `job_id` (numbers) — for run/job-specific methods
- `status` — filter for list methods
- `tail_lines` (default 200, min 1), `max_bytes` (default 65536, min 1024) — for log methods
- `output_path` — for `"download_job_log"`
- `page`, `per_page` (default 30, min 1)
### `actions_run_write` [W]
- `method` — `"dispatch_workflow"` | `"cancel_run"` | `"rerun_run"`
- `owner`, `repo` (required)
- `workflow_id` (for dispatch), `ref` (branch/tag for dispatch), `inputs` (object for dispatch)
- `run_id` (for cancel/rerun)
---
## Notifications
### `notification_read` [R]
- `method` — `"list"` | `"get"`
- `owner`, `repo` (optional, scopes to repo)
- `id` (thread ID for `"get"`)
- `status` — `"unread"` | `"read"` | `"pinned"`
- `subject_type` — `"Issue"` | `"Pull"` | `"Commit"` | `"Repository"`
- `since`, `before` (ISO 8601)
- `page`, `per_page` (default 30)
### `notification_write` [W]
- `method` — `"mark_read"` | `"mark_all_read"`
- `id` (thread ID for `"mark_read"`)
- `owner`, `repo` (optional scope)
- `last_read_at` (ISO 8601, defaults to now)
---
## Time Tracking
### `timetracking_read` [R]
- `method` — `"list_issue_times"` | `"list_repo_times"` | `"get_my_stopwatches"` | `"get_my_times"`
- `owner`, `repo` (for list methods)
- `issue_number` (for `"list_issue_times"`)
- `page`, `per_page` (default 30)
### `timetracking_write` [W]
- `method` — `"start_stopwatch"` | `"stop_stopwatch"` | `"delete_stopwatch"` | `"add_time"` | `"delete_time"`
- `owner`, `repo`, `issue_number` (optional depending on method)
- `time` (seconds, for `"add_time"`)
- `id` (entry ID, for `"delete_time"`)
---
## Packages
### `package_read` [R]
- `method` — `"list"` | `"list_versions"` | `"get"`
- `owner` (user or org, required)
- `type` (package type: `"container"` | `"npm"` | `"maven"` | `"pypi"` | `"cargo"` | `"generic"`) — required except for `"list"`
- `name` (package name, slashes auto-encoded) — required except for `"list"`
- `version` — for `"get"`
- `q` (search query)
- `page`, `per_page` (default 30, min 1)
### `package_write` [W]
- `method` — `"delete"` (only operation; irreversible)
- `owner`, `type`, `name`, `version` (all required)
---
## Wiki
### `wiki_read` [R]
- `method` — `"list"` | `"get"` | `"get_revisions"`
- `owner`, `repo` (required)
- `pageName` (for `"get"` and `"get_revisions"`)
### `wiki_write` [W]
- `method` — `"create"` | `"update"` | `"delete"`
- `owner`, `repo` (required)
- `pageName` (required for update/delete)
- `title` (required for create)
- `content` (for create/update)
- `message` (commit message)
---
## Version
### `get_gitea_mcp_server_version` [R]
No parameters. Returns the running server version string.

View File

@@ -0,0 +1,87 @@
---
topic: data-model
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Gitea Data Model
How issues, PRs, labels, milestones, and branches relate to each other — and the representation quirks that affect how you call tools.
## Issues and PRs share a number space
Issues and pull requests are the same entity type in Gitea's data model. They share a single sequential counter per repository: if issue #3 exists, there cannot be a PR #3 in the same repo. The `list_issues` tool returns both unless you pass `type: "issues"` or `type: "pulls"` to filter. A single-item `issue_read` response includes `is_pull: true` when the issue is backed by a pull request.
This matters for cross-linking: when you refer to `#5`, it might be either an issue or a PR. Use `issue_read method: "get"` and check `is_pull` to determine the type before deciding how to handle it.
## Label identity: names vs IDs
Labels have two identifiers that appear in different contexts:
- **Name** (string) — what appears in `issue_read` and `list_issues` responses; `labels: ["bug", "wontfix"]`
- **ID** (number) — what `issue_write` requires when applying labels; `labels: [3, 7]`
This mismatch is the single most common source of errors when managing labels. The MCP server slims label data in issue/PR responses to name-only strings. To apply labels to an issue or PR, you must first call `label_read method: "list_repo_labels"` to get the full label list (including IDs), then extract the IDs for the labels you want to apply.
The only tool that returns full label objects (including ID, color, description) is `label_read`.
## Milestone references
Milestones are repo-scoped and have both an **ID** and a sequential **number** within the repo. The tools use the numeric **ID** for all references:
- `issue_write` takes `milestone: <id>` (not a title, not a number)
- `milestone_read method: "get"` takes `id: <id>`
Milestone representations differ between issue and PR responses:
- In `issue_read` responses: `milestone: {id, title}` — an object with both fields
- In `pull_request_read` responses: `milestone: "title string"` — the title only, no ID
This inconsistency means you cannot extract a milestone's ID from a PR response. To get the milestone ID from a PR, you must look up milestones via `milestone_read method: "list"` and match by title.
## Label scope: repo vs org
Labels exist at two scopes:
- **Repo labels** — the default; scoped to a single repository; managed via `label_read/write` with `owner` + `repo`
- **Org labels** — scoped to an organization; shared across repos in the org; managed via `label_read/write` with `org`
Org labels have an additional `exclusive` boolean. When `exclusive: true`, applying one label in an exclusive group removes all other exclusive labels from the same group on the issue. This is Gitea's equivalent of a single-select category field.
When listing labels for labeling purposes, repo labels and org labels are listed separately. Issues in a repo can carry both.
## PR branches and cross-repo forks
For a PR within the same repository, `head` is just a branch name. For cross-repo PRs (from a fork), `head` uses the format `owner:branch`.
In `pull_request_write method: "create"`:
- `head` — the source (feature branch or fork branch)
- `base` — the merge target (usually `main` or `master`)
In `pull_request_read method: "get"` responses, `head` and `base` are full objects: `{ref, sha, repo: {full_name, description}}`. In `list_pull_requests` responses, they are bare ref strings.
## File SHA requirement
File operations in Gitea use SHA-based optimistic concurrency. To update or delete an existing file, you must provide the file's current `sha` — a content-addressed identifier returned by `get_file_contents`. Without it:
- `create_or_update_file` without `sha` is treated as a create; Gitea returns 409 if the file exists
- `delete_file` without `sha` returns a validation error
The workflow is always: `get_file_contents` → extract `sha` → pass to `create_or_update_file` or `delete_file`.
## Commit association on issues
Issues optionally carry a `ref` field — a branch name that associates the issue with ongoing work. This is set via `issue_write method: "create"` or `"update"` with the `ref` parameter. It is informational only; it does not create a branch or affect PR linking.
## PR review states
PR reviews move through a state machine:
1. Create a review in `"PENDING"` state (drafting inline comments)
2. Submit the review with a state: `"APPROVED"`, `"REQUEST_CHANGES"`, or `"COMMENT"`
3. A submitted review can be dismissed (not deleted)
The `stale` boolean on a review indicates the PR was pushed to after the review was submitted, making the review potentially outdated.
## Pagination and truncation
Repository tree (`get_repository_tree`) returns a `truncated: bool` field alongside `total_count`. When `truncated` is true, not all tree entries fit in one response — use `page` + `per_page` to paginate.
All list endpoints use cursor-less offset pagination (`page` integer). There is no `next_cursor` or `Link` header exposed through the MCP tools — iterate by incrementing `page` until you get fewer results than `per_page`.

View File

@@ -0,0 +1,193 @@
---
topic: examples
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Gitea MCP — Common Workflow Patterns
Canonical call sequences for the operations most likely to appear in a Gitea-managing skill.
## Create an issue with labels and a milestone
Labels and milestones must be referenced by numeric ID in write operations. Resolve them first.
```
1. label_read method: "list_repo_labels" owner: "Defame1297" repo: "holocron"
→ returns [{id: 3, name: "bug"}, {id: 7, name: "enhancement"}, ...]
2. milestone_read method: "list" owner: "Defame1297" repo: "holocron"
→ returns [{id: 1, title: "v1.0"}, ...]
3. issue_write method: "create"
owner: "Defame1297" repo: "holocron"
title: "Fix the widget"
body: "Description of the problem"
labels: [3] ← IDs, not names
milestone: 1 ← milestone ID
assignees: ["alice"]
```
## Apply labels to an existing issue
```
1. label_read method: "list_repo_labels" owner: ... repo: ...
→ map name → id for the labels you want
2. issue_write method: "add_labels"
owner: ... repo: ...
issue_number: 42
labels: [3, 7] ← IDs
```
To replace all labels atomically (remove existing, set new):
```
issue_write method: "replace_labels"
issue_number: 42
labels: [3, 7]
```
To remove a single label:
```
issue_write method: "remove_label"
issue_number: 42
label_id: 3 ← singular, not the array form
```
## Create a feature branch, push a file, open a PR
```
1. create_branch
owner: "Defame1297" repo: "holocron"
branch: "feat/my-feature"
old_branch: "main" ← defaults to repo default if omitted
2. get_file_contents (only needed if updating an existing file)
owner: ... repo: ... ref: "feat/my-feature" path: "README.md"
→ note the sha field
3. create_or_update_file
owner: ... repo: ...
path: "README.md"
content: "<base64-encoded content>"
message: "feat: update README"
branch_name: "feat/my-feature"
sha: "<sha from step 2>" ← required when updating; omit only for new files
4. pull_request_write method: "create"
owner: ... repo: ...
title: "feat: my feature"
body: "Description of changes"
head: "feat/my-feature"
base: "main"
labels: [7] ← label IDs, if desired
milestone: 1
reviewers: ["alice"]
```
## Merge a PR and clean up
```
1. pull_request_read method: "get_status"
owner: ... repo: ... pull_number: 12
→ check that status is passing before merge
2. pull_request_write method: "merge"
owner: ... repo: ... pull_number: 12
merge_style: "squash" ← or "merge", "rebase", etc.
message: "feat: my feature (#12)"
delete_branch: true ← clean up the feature branch post-merge
```
## Close an issue when a PR merges
Issues are not automatically closed when a PR merges in Gitea (unlike GitHub). Close them explicitly after merge:
```
issue_write method: "update"
owner: ... repo: ...
issue_number: 5
state: "closed"
```
## Create and manage a milestone
```
1. milestone_write method: "create"
owner: ... repo: ...
title: "v1.0"
description: "First stable release"
due_on: "2025-03-01T00:00:00Z"
2. Assign issues to it:
issue_write method: "update"
issue_number: 42
milestone: <id from step 1>
3. Close it when done:
milestone_write method: "update"
id: <milestone id>
state: "closed"
```
## List open PRs linked to a milestone
```
list_pull_requests
owner: ... repo: ...
state: "open"
milestone: <milestone id>
```
Note: `list_pull_requests` response items carry `milestone` as a bare title string, not an object. You cannot filter by milestone ID from the PR list response alone — pass the milestone ID as a query parameter instead.
## Resolve label name → ID without listing all labels
There is no direct lookup-by-name endpoint exposed through the MCP tools. The pattern is always:
```
label_read method: "list_repo_labels" per_page: 50
→ scan results for the target name → extract id
```
If you have more than 50 labels, paginate until found.
## Submit a code review
```
1. pull_request_review_write method: "create"
pull_number: 12
state: "PENDING"
commit_id: "<head SHA from PR get response>"
comments: [
{path: "src/foo.go", body: "Consider extracting this", new_line_num: 42}
]
→ returns review_id
2. pull_request_review_write method: "submit"
pull_number: 12
review_id: <from step 1>
state: "REQUEST_CHANGES"
body: "A few nits, see inline comments"
```
## Update a file when you don't know the current SHA
SHA is mandatory for file updates. If you skipped storing it:
```
get_file_contents
owner: ... repo: ...
ref: "main"
path: "the/file.md"
→ extract sha from response
create_or_update_file
...
sha: <extracted sha>
```
Do not guess or omit the SHA — the request will fail or create a duplicate.

View File

@@ -0,0 +1,65 @@
---
topic: overview
source_keys:
- gitea-mcp-repo
---
# Gitea MCP Server — Overview
The Gitea MCP server (gitea-mcp v1.3.0) wraps the Gitea REST API and exposes it as 55 MCP tools. It runs as a stdio process and is launched with `go run gitea.com/gitea/gitea-mcp@latest -t stdio`. The skill author interacts with it entirely through MCP tool calls — no direct HTTP or shell access is required.
## Tool registry architecture
Tools are split into two registries at startup: 31 read-only tools and 24 write tools. The `--read-only` flag suppresses all write tools; the `--tools` flag accepts a comma-separated allowlist and filters out any tool not named. In normal use (no flags), all 55 tools are available.
Many tools use a **method dispatch** pattern: a single MCP tool exposes multiple operations through a required `method` enum parameter. For example, `issue_write` handles `create`, `update`, `add_comment`, `edit_comment`, `add_labels`, `remove_label`, `replace_labels`, and `clear_labels`. This keeps the tool surface smaller while multiplexing related mutations. The `method` parameter is always required for dispatch tools and must be one of the documented enum values.
## Naming and parameter conventions
All repo-scoped tools require `owner` (string, the Gitea username or org) and `repo` (string, the repository name). These are always required and positional — never omit them.
Pagination is consistent across list tools: `page` (default 1) and `per_page` (default 30 for most tools, 20 for releases, 100 for org repos). Results are not automatically paginated — the caller must iterate pages.
Timestamps use ISO 8601 format (`2024-01-15T10:00:00Z`) for both input parameters (`since`, `before`, `deadline`, `due_on`) and response fields.
## Token scopes and tool availability
Three token scopes gate different tool groups:
- `write:issue` — enables issue, comment, label, and milestone operations (read and write)
- `write:repository` — enables PR, branch, file, release, and tag operations (read and write, because Gitea gates reads behind write scope for these)
- `read:user` — required for `get_me`, `list_my_repos`, `get_user_orgs`; absent from the default token config
Tools that require `read:user` but are called without it return an error or empty result. The workaround for repo discovery without `read:user` is `search_repos` with an `ownerID` filter, though `ownerID` requires knowing the numeric user ID in advance.
## Response shape philosophy
The MCP server returns **slimmed** response objects, not the full Gitea API JSON. Key simplifications:
- User fields are always a bare login string, never a user object
- Label fields are always a flat array of name strings (`["bug", "enhancement"]`), never label objects
- List responses drop `body` (description text) and some metadata fields to reduce token payload
- Single-item responses (get by ID or number) include full detail including body
This means you cannot get a user's display name or label colors from issue/PR responses — only the login or label name.
## Domain grouping
Tools are organized into these functional domains, each covered in detail in `api-reference.md`:
- **Issues** — `list_issues`, `search_issues`, `issue_read`, `issue_write`
- **Pull Requests** — `list_pull_requests`, `pull_request_read`, `pull_request_write`, `pull_request_review_write`
- **Labels** — `label_read`, `label_write`
- **Milestones** — `milestone_read`, `milestone_write`
- **Branches** — `list_branches`, `create_branch`, `delete_branch`
- **Files** — `get_file_contents`, `get_dir_contents`, `create_or_update_file`, `delete_file`, `get_repository_tree`
- **Commits** — `list_commits`, `get_commit`
- **Releases / Tags** — `list_releases`, `get_release`, `get_latest_release`, `create_release`, `delete_release`, `list_tags`, `get_tag`, `create_tag`, `delete_tag`
- **Search** — `search_repos`, `search_issues`, `search_users`, `search_org_teams`
- **Repos** — `create_repo`, `fork_repo`, `list_my_repos`, `list_org_repos`
- **User / Orgs** — `get_me`, `get_user_orgs`
- **Actions (CI)** — `actions_config_read`, `actions_config_write`, `actions_run_read`, `actions_run_write`
- **Notifications** — `notification_read`, `notification_write`
- **Time Tracking** — `timetracking_read`, `timetracking_write`
- **Packages** — `package_read`, `package_write`
- **Wiki** — `wiki_read`, `wiki_write`

View File

@@ -0,0 +1,22 @@
# Sources
## gitea-mcp-repo
- **URL:** https://gitea.com/gitea/gitea-mcp
- **Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags
- **Contributing files:** overview.md, api-reference.md, data-model.md, examples.md, troubleshooting.md
- **Status:** `extracted`
## gitea-mcp-slim-go
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/issue/slim.go, https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/pull/slim.go, https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/repo/slim.go
- **Description:** Slim response shape structs from gitea-mcp source; defines exactly which fields the MCP server returns for issues, PRs, branches, commits, tags, releases, and files
- **Contributing files:** api-reference.md, data-model.md, examples.md, troubleshooting.md
- **Status:** `extracted`
## gitea-api-docs
- **URL:** https://docs.gitea.com/api/1.20/
- **Description:** Gitea REST API swagger documentation covering underlying endpoints for issues, PRs, labels, milestones, and branches
- **Contributing files:** (see notes below)
- **Status:** `no content extracted` — source fetch timed out; all reference content derived from gitea-mcp source files which are authoritative for MCP tool usage

View File

@@ -0,0 +1,96 @@
---
topic: troubleshooting
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Gitea MCP — Troubleshooting
Known gotchas, source-level bugs, and error patterns for skill authors.
## Source-level typo: `review_scomments`
The `pull_request_read method: "get"` response includes a field called `review_scomments` (not `review_comments`). This is a misspelling in the slim.go source code of gitea-mcp v1.3.0. Do not expect `review_comments` to be present — the field is `review_scomments`. This is the count of inline review comments.
## Label ID vs name confusion
`issue_write` (methods: `add_labels`, `replace_labels`) takes `labels` as an array of **numbers** (IDs). Issue and PR read responses return labels as an array of **name strings**. These are never interchangeable.
If you pass name strings to `labels`, the call will either fail validation or silently apply no labels. Always resolve names to IDs first via `label_read method: "list_repo_labels"`.
## Milestone representation inconsistency
The `milestone` field in responses differs by entity type:
- `issue_read` response: `milestone: {id: 1, title: "v1.0"}` — object
- `pull_request_read` response: `milestone: "v1.0"` — string (title only)
You cannot get a milestone's ID from a PR response. If you need the ID, call `milestone_read method: "list"` and match by title.
## File update requires current SHA
`create_or_update_file` and `delete_file` both require the file's current content SHA. Without it:
- Updating: Gitea treats the call as a create. If the file exists, returns HTTP 409 (Conflict).
- Deleting: returns HTTP 422 (Unprocessable entity) — the SHA is a required field.
Always call `get_file_contents` first to retrieve the SHA. The SHA is in the `sha` field of the response (not `content.sha` — it's top-level).
## `list_my_repos` requires `read:user` scope
With only `write:issue` and `write:repository` scopes, `list_my_repos` returns an error. Use `search_repos` with an `ownerID` filter instead. The `ownerID` is the numeric user ID — you cannot get it from `get_me` either (same `read:user` requirement). If ownerID is unknown, use `search_repos` with the username in the query.
## `get_me` requires `read:user` scope
There is no way to discover the current user's identity (login, ID) with `write:issue` + `write:repository` scopes only. Hardcode the owner/username in the skill or require it as an input parameter.
## Method dispatch: `"update"` vs `"edit"` on milestones
`milestone_write` accepts both `"update"` and `"edit"` as method values for updating a milestone — they map to the same operation. Use `"update"` for consistency with the issue and PR tools.
## Draft PR behavior
`pull_request_write method: "create"` with `draft: true` implements draft by prepending `"WIP:"` to the PR title (not via a dedicated Gitea API flag). This means:
- The title returned by the API will have the `"WIP:"` prefix
- To un-draft, use `"update"` and pass the title without the prefix
This differs from GitHub's draft PR mechanism; draft state is not a first-class boolean field.
## `delete_release` takes numeric ID, not tag name
`delete_release` requires the numeric `id` from the release object, not the `tag_name` string. Call `list_releases` or `get_release` first to get the numeric ID.
## Cross-repo PR `head` format
For PRs originating from a fork, `head` must be `"fork-owner:branch-name"`. Using just the branch name will result in Gitea looking for the branch in the base repo and failing with 422.
## Pagination is not automatic
List tools return one page at a time. There is no cursor, `Link` header, or auto-pagination in the MCP layer. When you need complete results (e.g., all labels to build a name→ID map), iterate `page: 1`, `page: 2`, etc. until the result count is less than `per_page`.
## HTTP error patterns
The MCP server surfaces HTTP error codes from the Gitea REST API:
| Code | Meaning in this context |
|---|---|
| 401 | Token is invalid, expired, or missing entirely |
| 403 | Token lacks the required scope for this operation |
| 404 | Resource not found — also returned by some endpoints when scope is insufficient |
| 409 | Conflict — file already exists (create without SHA), branch already exists, duplicate PR |
| 422 | Unprocessable entity — missing required field (e.g. SHA on file ops), invalid enum value |
| 500 | Gitea server error — usually transient |
404 can mask a permissions error: some Gitea endpoints return 404 instead of 403 when the token has insufficient scope, to avoid leaking resource existence.
## `list_issues` with `milestones` filter
The `milestones` parameter on `list_issues` accepts milestone names or IDs as an array. Using IDs is more reliable — milestone names are mutable. Always prefer filtering by milestone ID when programmatically filtering.
## `per_page` defaults vary
Not all endpoints share the same default `per_page`:
- Most tools: 30
- `list_releases`, `list_tags`: 20
- `list_org_repos`: 100
When building result-count-aware logic, do not assume 30 — check the tool's documented default.

View File

@@ -0,0 +1,38 @@
# gitea
Dispatch skill for managing a Gitea repo — issues, PRs, milestones, labels, and branches — from within Claude Code.
## Files
| File | Purpose |
|---|---|
| `SKILL.md` | Skill definition — dispatch table, gotchas, execution steps |
| `references/token-access.md` | Token scope inventory — what works vs. what needs additional scopes |
## Usage
```
/gitea # status: open issues + open PRs
/gitea issue # create issue from conversation context
/gitea issue <N> # get issue details
/gitea issue close <N> # close issue
/gitea issue comment <N> # add comment from conversation context
/gitea label <N> Kind/Bug # apply labels by name (resolves IDs automatically)
/gitea milestone # list milestones
/gitea milestone create <title> # create milestone
/gitea pr # create PR from current branch → main
/gitea pr <N> # get PR status and diff summary
/gitea pr merge <N> # squash-merge PR, delete branch
/gitea branch # list branches
/gitea branch create <name> # create branch from current branch
```
## Requirements
- Gitea MCP server configured in `~/.claude.json` with `write:issue` and `write:repository` token scopes
- `git remote origin` pointing to the Gitea instance (used to derive owner/repo at runtime)
## Scope (v1)
In scope: issues, milestones, labels, PRs, branches, status.
Out of scope: releases, CI/Actions, wiki, file operations, notifications, packages, time tracking.

View File

@@ -0,0 +1,141 @@
---
name: gitea
description: >
Use when the user wants to interact with Gitea — create or update issues,
open or merge pull requests, manage labels and milestones, list branches,
or check repo status. Triggers on: "create an issue", "open a PR", "what's
open", "label this issue", "create a milestone", "merge the PR", "list
branches", "close this issue" — even when the user doesn't say "Gitea"
explicitly. Owner and repo are derived automatically from the git remote;
no config required. Do not use for releases, CI/Actions, wiki, file
operations, notifications, or package management — those are out of scope.
compatibility: Requires Gitea MCP server configured in ~/.claude.json with write:issue and write:repository token scopes. Requires git remote "origin" pointing to the Gitea instance.
allowed-tools: Bash mcp__gitea__list_issues mcp__gitea__search_issues mcp__gitea__issue_read mcp__gitea__issue_write mcp__gitea__label_read mcp__gitea__label_write mcp__gitea__milestone_read mcp__gitea__milestone_write mcp__gitea__list_pull_requests mcp__gitea__pull_request_read mcp__gitea__pull_request_write mcp__gitea__list_branches mcp__gitea__create_branch
metadata:
category: integration
---
## Gotchas
- **Label writes take IDs, reads return names.** `issue_write` (add_labels, replace_labels) requires `labels: [3, 7]` (numeric IDs). Issue and PR responses return `labels: ["bug", "enhancement"]` (name strings). These are never interchangeable. Always call `label_read method: "list_repo_labels"` first and resolve names → IDs before any label write.
- **Issues and PRs share a number space.** `#5` might be an issue or a PR — there is only one counter per repo. `list_issues` returns both unless you pass `type: "issues"` or `type: "pulls"`. Check `is_pull` on a single-item `issue_read` response to determine the type.
- **Milestone write takes ID, not title.** `issue_write` takes `milestone: <numeric id>`. The title is not accepted. In `issue_read` responses the milestone is `{id, title}`, but in `pull_request_read` responses it's a bare title string — you cannot recover the ID from a PR response. Call `milestone_read method: "list"` and match by title if you need the ID from a PR context.
- **`get_me` is unavailable** with the current token (`write:issue, write:repository` only — `read:user` is missing). Owner and repo must always be derived from the git remote, never from `get_me` or `list_my_repos`.
- **Issues are not auto-closed when a PR merges.** Unlike GitHub, Gitea does not close linked issues on merge. Close explicitly with `issue_write method: "update" state: "closed"` after merging.
- **Pagination is manual.** List tools return one page at a time — no auto-pagination. When building complete datasets (e.g. all labels for name→ID mapping), iterate `page: 1, 2, ...` until result count < `per_page`.
- **`pull_request_read method: "get"` returns `review_scomments`, not `review_comments`.** This is a source-level typo in gitea-mcp v1.3.0. Do not access `review_comments` — it will always be undefined. Use `review_scomments`.
- **Cross-repo fork PRs require `head` as `"fork-owner:branch-name"`.** A bare branch name causes Gitea to search the base repo and return 422. The `pr create` dispatch assumes same-repo PRs (bare branch name). For fork-based PRs, pass `head` explicitly in the `owner:branch` format.
- **`draft: true` on PR create prepends `WIP:` to the title.** There is no first-class draft field — Gitea implements draft PRs via title prefix. To un-draft, call `pull_request_write method: "update"` and pass the title without the `WIP:` prefix. This differs from GitHub's draft PR model.
- **HTTP 404 may mean 403.** Gitea hides permission errors as not-found to avoid leaking resource existence. If a tool call returns 404 unexpectedly, check `references/token-access.md` before assuming the resource does not exist.
## Step 1 — Resolve owner and repo
Before any tool call, extract `owner` and `repo` from the git remote:
```bash
git remote get-url origin
```
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
## Step 2 — Dispatch
Route on the first argument:
| Invocation | Action |
|---|---|
| `/gitea` (no args) | **Status** — list open issues + open PRs |
| `/gitea issue` | Create issue from conversation context |
| `/gitea issue <N>` | Get issue details |
| `/gitea issue close <N>` | Close issue |
| `/gitea issue comment <N>` | Add comment from conversation context |
| `/gitea label <N> <names...>` | Apply named labels to issue/PR |
| `/gitea milestone` | List milestones |
| `/gitea milestone create <title>` | Create milestone |
| `/gitea pr` | Create PR from current branch → main |
| `/gitea pr <N>` | Get PR status and diff summary |
| `/gitea pr merge <N>` | Merge PR (squash, delete branch) |
| `/gitea branch` | List branches |
| `/gitea branch create <name>` | Create branch from current branch |
## Step 3 — Execute
### Status (default)
Call `list_issues` twice in parallel — once with `type: "issues"` and once with `type: "pulls"`, both `state: "open"`. Report as two sections.
### issue (create)
Extract title and body from conversation context. Use the most recent task, bug description, grill output, or explicit statement. If no body text is available from context, fall back to empty string. Fire immediately — no confirmation step.
Set `ref` to the current branch name (`git branch --show-current`) if a branch is already checked out for this work.
Call `issue_write method: "create" title: <extracted> body: <extracted or ""> ref: <current-branch-if-applicable>`.
### issue <N>
Call `issue_read method: "get" issue_number: <N>`. If the response includes `is_pull: true`, the number refers to a PR — report it as such and offer `pr <N>` for a full PR summary.
### issue close <N>
Call `issue_write method: "update" issue_number: <N> state: "closed"`. There is no `method: "close"` — using a non-existent method will error.
### issue comment <N>
Extract the comment body from conversation context (same sourcing as issue create). Call `issue_write method: "add_comment" issue_number: <N> body: <extracted>`.
### label <N> <names...>
1. Call `label_read method: "list_repo_labels"` — paginate until complete if > 30 labels.
2. Match each provided name (case-insensitive) against the label list → collect IDs.
3. Call `issue_write method: "add_labels" issue_number: <N> labels: [<matched IDs>]`.
4. Report applied labels and warn on any names that did not match, listing available labels.
Do not fail the operation because of unmatched names — apply what matches.
### milestone
Call `milestone_read method: "list"`. Report each milestone as: id, title, state (open/closed), open issue count, closed issue count.
### pr (create)
1. `git branch --show-current` → head branch.
2. Title: extract from conversation context; fall back to the last commit message (`git log -1 --pretty=%s`).
3. Body: extract from conversation; fall back to empty.
4. Call `pull_request_write method: "create" head: <branch> base: "main" title: <derived in step 2> body: <derived in step 3>`.
Note: this dispatch assumes a same-repo PR (bare branch name for `head`). For cross-repo fork PRs, `head` must be `"fork-owner:branch-name"` — see Gotchas.
### pr <N>
Call `pull_request_read method: "get"` and `pull_request_read method: "get_status"` in parallel (both take `pull_number: <N>`). Report: title, state, draft/merged flag, head → base, labels, CI status from get_status. Note: `milestone` in PR responses is a bare title string, not an object — you cannot extract a milestone ID from it.
### pr merge <N>
First call `pull_request_read method: "get_status" pull_number: <N>`. If CI status is failing, report it and warn the user — but do not block the merge unless they say to stop.
Then call `pull_request_write method: "merge" pull_number: <N> merge_style: "squash" delete_branch: true`. To use a different merge style, the user must specify it explicitly.
### milestone create <title>
Call `milestone_write method: "create" title: <title>`. Report the created milestone ID — it will be needed for assigning issues.
### branch
Call `list_branches`. Report each branch as: name, protected (bool).
### branch create <name>
Get the current local branch: `git branch --show-current`. Call `create_branch branch: <name> old_branch: <current-branch>`. This forks the new branch from where you are, not from the repo's default branch. If the user specifies a different base explicitly, use that instead.
## Step 4 — Report
For reads: display results as a compact table or numbered list — include number, title, labels, and milestone for issues/PRs.
For writes: confirm what was created/updated with the Gitea issue/PR number and URL if returned.
For errors: surface the HTTP code and message. 404 from some endpoints may actually mean insufficient token scope (Gitea hides 403 as 404 to avoid leaking resource existence).
If label resolution fails partially, always report which names were applied and which were skipped.
If token scope issues are suspected, read `references/token-access.md` for the full scope inventory.

View File

@@ -0,0 +1,94 @@
---
topic: token-access
description: Gitea MCP token scope inventory — what works with the configured token vs. what requires additional scopes
---
# Gitea MCP Token Access
## Current token scope
- write:issue
- write:repository
## What works
### Repository discovery
| Tool | Works | Notes |
| -------------------------| ----------| ----------------------------|
| `search_repos` | ✅ | Finds repos by keyword |
| `list_my_repos` | ❌ | Needs `read:user` |
| `list_org_repos` | ❌ | Needs `read:user` (inferred from `get_user_orgs` failure) |
| `get_repository_tree` | ✅ | With `write:repository` |
| `get_dir_contents` | ✅ | With `write:repository` |
| `get_file_contents` | ✅ | With `write:repository` |
| `create_or_update_file` | ✅ | Core `write:repository` op |
| `delete_file` | ✅ | Core `write:repository` op |
### Issues
| Tool | Works | Notes |
| ------------------------------------------------------------------------| -------| -----------------------------|
| `list_issues` | ✅ | Full list with state filter |
| `search_issues` | ✅ | Cross-repo search |
| `issue_read` (get) | ✅ | |
| `issue_read` (get_comments) | ✅ | |
| `issue_read` (get_labels) | ✅ | |
| `issue_write` (create) | ✅ | Core `write:issue` op |
| `issue_write` (update) | ✅ | |
| `issue_write` (add_comment) | ✅ | |
| `issue_write` (edit_comment) | ✅ | |
| `issue_write` (add_labels, remove_label, replace_labels, clear_labels) | ✅ | |
| `label_read` | ✅ | |
| `label_write` | ✅ | |
| `milestone_read` | ✅ | |
| `milestone_write` | ✅ | |
### Pull Requests
| Tool | Works | Notes |
|---|---|---|
| `list_pull_requests` | ✅ | |
| `pull_request_read` (get, get_diff, get_files, get_status, get_reviews) | ✅ | |
| `pull_request_write` (create, update, close, reopen, merge, update_branch) | ✅ | Needs `write:repository` |
| `pull_request_review_write` | ✅ | |
### Branches & Commits
| Tool | Works | Notes |
|---|---|---|
| `list_branches` | ✅ | |
| `create_branch` | ✅ | `write:repository` |
| `delete_branch` | ✅ | `write:repository` |
| `list_commits` | ✅ | |
| `get_commit` | ✅ | |
### Releases & Tags
| Tool | Works | Notes |
|---|---|---|
| `list_releases`, `get_release`, `get_latest_release` | ✅ | |
| `create_release`, `delete_release` | ✅ | `write:repository` |
| `list_tags`, `get_tag`, `create_tag`, `delete_tag` | ✅ | `write:repository` |
### Actions (CI)
| Tool | Works | Notes |
|---|---|---|
| `actions_config_read` (list_repo_secrets) | ❌ | "user should be the owner of the repo" — token auth not accepted for secrets |
| `actions_config_write` | untested (write — not tested to avoid side effects) | |
| `actions_run_read` (list_workflows) | ✅ empty | No workflows defined; returns `{total_count:0,workflows:[]}` |
| `actions_run_read` (list_runs) | ❌ | "user should be the owner of the repo" — 403 |
| `actions_run_write` | untested (write — not tested to avoid side effects) | |
### User & Identity
| Tool | Works | Notes |
|---|---|---|
| `get_me` | ❌ | Needs `read:user` |
| `search_users` | ❌ | Needs `read:user` |
| `get_user_orgs` | ❌ | Needs `read:user` and `read:organization` |
### Other
| Tool | Works | Notes |
| ---------------------------------------| -----------------------------------------------------| ------------------------------------------|
| `wiki_read` | ❌ | 404 — wiki not enabled for this repo |
| `wiki_write` | untested (write — not tested to avoid side effects) | |
| `notification_read` | ❌ | Needs `read:notification` scope |
| `notification_write` | untested (write — not tested to avoid side effects) | |
| `timetracking_read` (list_repo_times) | ✅ empty | No tracked times; returns string message |
| `timetracking_write` | untested (write — not tested to avoid side effects) | |
| `package_read` | ❌ | Needs `read:package` scope |
| `package_write` | untested (write — not tested to avoid side effects) | |