Skip to main content
Glama

npm version codecov license Node TypeScript MCP

English | 中文文档

gitea-mcp is a Model Context Protocol (MCP) server that exposes Gitea repository operations as tools. Once connected to an MCP client (Claude Desktop, opencode, Cursor, etc.), an AI assistant can list, create, update, and delete issues, labels, milestones, comments, pull requests, releases, Actions runs, and wiki pages on your Gitea instance — all through natural language.

The server communicates over stdio and wraps the Gitea REST API (/api/v1). GitLab (gitlab.com and self-managed) is supported as a second platform — see GitLab support.

Features

  • GitLab support — the same tool set against gitlab.com and self-managed GitLab instances (MCP_PLATFORM=gitlab or the GITLAB_* env contract)

  • Full Gitea project management — issues, labels, milestones, comments, and topics via natural language

  • Zero-config auto-discovery — reads baseUrl, owner, repo, and token from the project's git config; one global install serves many repos

  • Multi-source auth with failover — tries the GITEA_REPO_URL userinfo, [gitea] config tokens, GITEA_TOKEN, then git's own credential machinery (git credential fill — store file or OS keychain), advancing automatically on 401/403

  • Action-scoped skills — ships one skill per workflow (find, create, label, comment, plan milestones, …) for opencode, Claude Code, Cursor, and more

  • Client-agnostic — works with any stdio-based MCP client; ships guidance prompts and on-demand reference resources too

Related MCP server: forgejo-mcp

Table of Contents

Requirements

  • Node.js ≥ 24 — uses the global fetch

  • git ≥ 2.46 on PATH — used for credential discovery (git config get / git credential fill; git credential fill also honors every configured credential helper, including OS keychains). When git cannot be used at all, discovery falls back to the env-only sources (GITEA_REPO_URL / GITEA_TOKEN) / anonymous mode — gitea_status reports gitAvailable: false. On git < 2.46, the .git/config [gitea] token source silently fails (exit codes are indistinguishable); credential helpers and the env sources still work.

  • A Gitea instance (self-hosted or Gitea Cloud) — or a GitLab instance (gitlab.com or self-managed) — reachable over HTTP

  • A Gitea API token (or a git credential) for anything beyond reading public repositories

Installation

From npm (npmjs.com)

npm install -g @amonstack/gitea-mcp

Or run directly without global install:

npx @amonstack/gitea-mcp

From GitHub Packages

Each release is also published to GitHub Packages. Route the @amonstack scope there once, then install:

echo "@amonstack:registry=https://npm.pkg.github.com" >> ~/.npmrc
npm install -g @amonstack/gitea-mcp

Build from source

git clone https://github.com/amonstack/gitea_mcp.git
cd gitea_mcp
npm ci
npm run build
node dist/cli.js

Configuration

All variables are optional — gitea-mcp auto-discovers the Gitea instance, repository, and token from the project's local git config so a single global install can serve many projects. Set them only to override the discovery or to restrict the tool surface.

Variable

Required

Description

GITEA_BASE_URL

No

Gitea instance URL (e.g. https://gitea.example.com). Auto-detected from the project's git remote when omitted.

GITEA_TOKEN

No

Gitea API access token. One of several auth candidates; tried after the GITEA_REPO_URL userinfo and a .git/config [gitea] token, and before git's credential machinery (see Token discovery).

GITEA_REPO_URL

No

One self-contained credentialed clone URL — https://<user>:<token>@<host>[:<port>]/<owner>/<repo>.git — carrying the instance URL, default owner/repo, and an auth candidate in a single variable. Each part sits below its explicit override (GITEA_BASE_URL, GITEA_DEFAULT_OWNER, GITEA_DEFAULT_REPO) and above the git remote; parsed in-memory, works without git, and the embedded secret never appears in any output.

GITEA_DEFAULT_OWNER

No

Default repository owner — skip passing owner on every call

GITEA_DEFAULT_REPO

No

Default repository name — skip passing repo on every call

GITEA_UPLOAD_ROOT

No

Root directory that attachment uploads (create_issue_attachment / create_issue_comment_attachment) may read from. Defaults to the server's working directory; the resolved path must stay inside this root.

MCP_PLATFORM

No

Which platform this server process serves: gitea (default) or gitlab. Overrides the auto-detection described in GitLab support.

MCP_TOOL_ALLOWLIST

No

Comma-separated snake_case tool names the server may expose (entries are trimmed and matched exactly). Unset or empty keeps every tool available; an entry naming no tool on the active platform aborts startup with a Fatal error.

GITLAB_BASE_URL

No

GitLab instance URL (e.g. https://gitlab.example.com). Auto-detected from the project's git remote when omitted; its presence (without a GITEA_* connection variable) selects GitLab mode.

GITLAB_TOKEN

No

GitLab API access token. One of several auth candidates; tried after the GITLAB_REPO_URL userinfo and a .git/config [gitlab] token, and before git's credential machinery. Always sent as Authorization: Bearer <token>.

GITLAB_REPO_URL

No

GitLab counterpart of GITEA_REPO_URL — one credentialed clone URL supplying the instance URL, default owner/project, and a Bearer auth candidate. Its presence (without a GITEA_* connection variable) selects GitLab mode.

GITLAB_DEFAULT_OWNER

No

Default project owner (GitLab mode) — skip passing owner on every call

GITLAB_DEFAULT_REPO

No

Default project name (GitLab mode) — skip passing repo on every call

GitLab support

gitea-mcp also runs against GitLab (gitlab.com and self-managed). One server process serves one platform — to use both, register two MCP client entries. The platform is selected at startup:

  1. MCP_PLATFORM=gitlab (or gitea) wins when set;

  2. otherwise GitLab is auto-selected when any of GITLAB_BASE_URL, GITLAB_TOKEN, or GITLAB_REPO_URL is set and no GITEA_* connection variable is;

  3. the default remains gitea (existing configurations behave unchanged).

In GitLab mode the same 68 business tool names run against the GitLab REST API v4 (/api/v4), plus configure_gitlab / gitlab_status instead of configure_gitea / gitea_status. Discovery mirrors the Gitea contract: a GITLAB_REPO_URL repo URL, a [gitlab "<baseUrl>"] token git-config entry, GITLAB_TOKEN, and git credential fill — every credential is sent only as Authorization: Bearer <token> (never as a URL query parameter).

Coverage notes — operations without a GitLab counterpart fail with a typed GitLabUnsupportedError, and Premium/Ultimate-gated operations fail with GitLabTierError (never a raw API error):

  • Fully supported: issues, comments (list/create), labels, milestones, topics, merge requests, releases (list/create/by-tag), wiki pages, and pipelines (the Actions group: list/get/cancel/rerun).

  • Tier-gated (GitLab Premium/Ultimate): the issue dependency/block tools (list_issue_dependencies, add/remove_issue_dependency, list_issue_blocks, add/remove_issue_block, check_issue_blocked) — GitLab Free rejects the blocking link types.

  • Not available on GitLab: the issue attachment tools; update_comment / delete_comment (GitLab notes are addressed per-issue); get_release, update_release, delete_release (GitLab releases are addressed by tag_name — use get_release_by_tag); rerun_action_run_failed_jobs; list_wiki_revisions; merge_pull_request with rebase / rebase-merge; and the draft/prerelease release flags, update_repo's website/private, wiki message, and search_issues labels parameters.

  • Addressing follows GitLab conventions: issues and merge requests by project-scoped iid, milestones and pipelines by numeric ID, releases by tag_name, wiki pages by slug; responses return GitLab's native JSON (iid, web_url, references, …).

  • The bundled action skills and the three guide resources teach Gitea specifics and are served on the Gitea platform only; GitLab guidance ships via the server instructions instead.

How auto-discovery works

On start, gitea-mcp reads <cwd>/.git/config and derives:

  • Instance URL — from the selected remote's host. SSH remotes (git@host:owner/repo) resolve to https://host. Override with GITEA_BASE_URL.

  • owner / repo — from the selected remote's URL. Override with GITEA_DEFAULT_OWNER / GITEA_DEFAULT_REPO, or detect ad hoc with the resolve_repo tool.

  • Remote selection — the upstream remote is preferred, falling back to origin, then any other remote. Both are reported by resolve_repo when they differ.

A GITEA_REPO_URL value — one credentialed clone URL of the form https://<user>:<token>@<host>[:<port>]/<owner>/<repo>.git — is a self-contained alternative to the whole chain above: it supplies the instance URL, the default owner/repo, and an auth candidate at once (parsed in-memory, so it works even when git is unavailable). Its parts sit below the explicit GITEA_BASE_URL / GITEA_DEFAULT_OWNER / GITEA_DEFAULT_REPO overrides and above the git remote, and the raw URL is never echoed: resolve_repo strips embedded userinfo from the remote URLs it reports.

If the current directory has no git remote and neither GITEA_BASE_URL nor GITEA_REPO_URL is set, the server starts in an unconfigured state — tools/list is fully available, but business tools return a NotConfiguredError on invocation. Use the configure_gitea tool to set the connection at runtime (session-scoped, never persisted), or run from inside a cloned Gitea repository, or set GITEA_BASE_URL / GITEA_TOKEN explicitly.

Token discovery

gitea-mcp collects authentication candidates from four sources, in this priority order — and asks git itself for the stored ones, so every credential helper you have configured works (including OS keychains: wincred, osxkeychain, libsecret):

  1. The GITEA_REPO_URL repo URL, when it points at the instance host — its embedded user:secret userinfo becomes the top-priority candidate, tried with the same username heuristic as a git credential (basic first for a real-looking username, token first for oauth2 / x-oauth-basic / a token stored in the username position).

  2. A [gitea "<baseUrl>"] section in .git/config (a bare [gitea] section is a host-wide fallback), read via git config get --url=<baseUrl> gitea.token:

    [gitea "https://gitea.example.com"]
        token = <your-token>

    Always sent as Authorization: token <token>.

  3. The GITEA_TOKEN environment variable — also sent as Authorization: token. When the git binary is not usable at runtime, the env sources above are the only remaining candidates.

  4. The credential git itself would use for the instance host, retrieved via git credential fill (config chain + credential helpers — the store file, OS keychains, or any helper you configured). With several identities stored for the host, git picks the one it would use; configure_gitea's username narrows the lookup to that identity.

A git credential's password field may hold a real PAT, an account password, or an OAuth token — git stores whatever was typed at the prompt, and the server cannot tell them apart statically. So the credential is tried under two authentication schemes:

  • Authorization: Basic <base64(user:pass)> — works for account passwords and PATs alike (Gitea checks that the username matches the secret's owner).

  • Authorization: token <secret> — works only for real PATs.

The order is chosen by a username heuristic: a convention username (oauth2, x-oauth-basic, or empty) tries token first; a real-looking username (e.g. alice) tries basic first.

Fault tolerance. On 401/403 the server advances to the next scheme/candidate and retries the same request; once a combination succeeds it is locked for the rest of the session (no re-probing). Non-auth errors (404, 500, network) propagate immediately and do not trigger a retry.

Diagnostics. The gitea_status tool (see Repository Helpers) returns a redacted view of the current state — which candidate is active, which are exhausted, the last status seen — without ever exposing the secret. Use it to troubleshoot a 401 instead of guessing.

If no source resolves a credential, the server still starts anonymously. Public repositories may be read; private repos and write operations return 401 — use the gitea-configure skill to guide setup, or set GITEA_TOKEN.

When GITEA_DEFAULT_OWNER and GITEA_DEFAULT_REPO are set, you can omit the owner and repo parameters in tool calls. The resolve_repo tool can also auto-detect them from a local git repository.

MCP Client Setup

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "gitea-mcp": {
      "command": "gitea-mcp",
      "env": {
        "GITEA_BASE_URL": "https://gitea.example.com",
        "GITEA_TOKEN": "your-token-here",
        "GITEA_DEFAULT_OWNER": "my-org",
        "GITEA_DEFAULT_REPO": "my-repo"
      }
    }
  }
}

If you built from source, use node /path/to/gitea-mcp/dist/cli.js instead.

opencode

Add to your opencode MCP configuration:

{
  "mcpServers": {
    "gitea-mcp": {
      "command": "gitea-mcp",
      "env": {
        "GITEA_BASE_URL": "https://gitea.example.com",
        "GITEA_TOKEN": "your-token-here"
      }
    }
  }
}

If you built from source, use node /path/to/gitea-mcp/dist/cli.js instead.

opencode and other AI tools can load native skills — one per action (find, create, update, label, comment, summarize, plan milestones, resolve repo) — that teach the assistant the safest workflow for that action, including pre-use checks and pitfalls. Install them once with the init command, targeting your tool (--tool, default opencode):

gitea-mcp init                      # opencode (global ~/.config/opencode/skills/)
gitea-mcp init --tool claude        # Claude Code (~/.claude/skills/)
gitea-mcp init --tool cursor        # Cursor (~/.cursor/skills/)
gitea-mcp init --project            # this project (./.<tool>/skills/)
gitea-mcp init --dir /exact/path    # custom location

Supported --tool values: amazon-q, antigravity, auggie, claude, cline, codex, codebuddy, continue, costrict, crush, cursor, factory, gemini, github-copilot, iflow, kimi, kilocode, opencode, qoder, qwen, roocode, windsurf. Paths follow each tool's conventional skills directory; use --dir for an exact location. Then restart your tool. See AI Guidance & Skills below.

Other MCP Clients

Any client that supports stdio-based MCP servers can use gitea-mcp. After installation, run it from inside a cloned Gitea repository (config is auto-discovered):

cd /path/to/your/gitea-repo
gitea-mcp

Or set the variables explicitly if you prefer:

export GITEA_BASE_URL="https://gitea.example.com"
export GITEA_TOKEN="your-access-token"
gitea-mcp

Available Tools

The tables below describe the tool contract. On the GitLab platform the same 68 business tools run against the GitLab REST API v4 (plus configure_gitlab / gitlab_status instead of configure_gitea / gitea_status) — per-tool coverage differences are documented in GitLab support.

Issues

Tool

Description

list_issues

List issues (filter by state, labels, page/limit)

get_issue

Get a single issue by index (issue number)

create_issue

Create an issue with title, body, assignee, labels, milestone

update_issue

Update issue fields or state

delete_issue

Delete an issue by index

search_issues

Search across repositories by query, type, state, labels

Issue Dependencies

Tool

Description

list_issue_dependencies

List the issues that BLOCK an issue (blocked by), with page/limit

add_issue_dependency

Make an issue depend on (be blocked by) another issue — dep_index is the blocker

remove_issue_dependency

Remove a dependency so an issue is no longer blocked by dep_index

list_issue_blocks

List the issues that are BLOCKED BY an issue (blocking), with page/limit

add_issue_block

Make an issue block another issue — index is the blocker, dep_index is blocked

remove_issue_block

Remove a block so dep_index is no longer blocked by index

check_issue_blocked

Check whether an issue is blocked by open dependencies — returns blocked, blockers, total_dependencies, open_blockers in one call (aggregates list_issue_dependencies internally)

Note: dependency tools require the repo to enable enable_issue_dependencies (a 404 means it is off). dep_owner / dep_repo default to the same repo as the issue but may point at another repo (the instance must enable AllowCrossRepositoryDependencies). Mutations return the path issue as JSON.

Comments

Tool

Description

list_comments

List comments on an issue

create_comment

Add a comment to an issue

update_comment

Update a comment by id

delete_comment

Delete a comment by id

Issue Attachments

Tool

Description

create_issue_attachment

Upload a local file (file_path) as an attachment on an issue (index), optional name. The path is confined to the upload root (cwd or GITEA_UPLOAD_ROOT), extension-allow-listed, and size-capped

list_issue_attachments

List the attachments on an issue

get_issue_attachment

Get one attachment's metadata by attachment_id

edit_issue_attachment

Rename an attachment by attachment_id

delete_issue_attachment

Delete an attachment by attachment_id

create_issue_comment_attachment

Upload a local file as an attachment on a comment (comment_id), optional name; same path confinement as create_issue_attachment

Note: attachment tools read file_path from the machine running gitea-mcp. Instances can disable attachments (a 404 means the feature is off) and cap the upload size (oversize fails 413/422).

Labels

Tool

Description

list_labels

List repository labels

create_label

Create a label (name, color, description)

update_label

Update a label by id

delete_label

Delete a label by id

add_issue_labels

Add labels to an issue by name

remove_issue_label

Remove a label from an issue by label id

replace_issue_labels

Replace all labels on an issue

clear_issue_labels

Remove all labels from an issue

Milestones

Tool

Description

list_milestones

List milestones (filter by state)

get_milestone

Get a milestone by id

create_milestone

Create a milestone with title, description, due_on

update_milestone

Update milestone fields or state

delete_milestone

Delete a milestone by id

Topics

Tool

Description

list_topics

List a repository's topics (tags)

replace_topics

Replace ALL topics with the given list (pass [] to clear)

add_topic

Add a single topic by name

remove_topic

Remove a single topic by name

Pull Requests

Tool

Description

list_pull_requests

List pull requests in a repo (filter by state, labels, sort, milestone)

get_pull_request

Fetch one pull request by its number

create_pull_request

Create a pull request (title, head, base, body)

update_pull_request

Edit a pull request or close/reopen it (state: open | closed)

merge_pull_request

Merge a pull request (strategy: merge | squash | rebase | rebase-merge) — irreversible

is_pull_merged

Check whether a pull request has been merged

list_pull_commits

List the commits in a pull request

list_pull_files

List the files changed in a pull request

Note: a pull request shares its number space with issues (PR #N == Issue #N). Comments, labels, and milestones on a PR reuse the issue tools — pass the PR number as the index. Only PR-specific operations use the tools above.

Actions

Tool

Description

list_action_runs

List Gitea Actions workflow runs (filter by status, branch, event, actor, head_sha)

get_action_run

Get a single workflow run by runId — check status before cancel/rerun

cancel_action_run

Cancel an active run (queued, in_progress, running) — partially destructive

rerun_action_run

Rerun an entire completed run — requires Gitea 1.26.0+

rerun_action_run_failed_jobs

Rerun only the failed jobs of a completed run — requires Gitea 1.26.0+

Note: cancel only works on active runs; rerun only works on completed runs. Always call get_action_run first to verify the current status, and confirm the runId with the user before cancelling or rerunning.

Releases

Tool

Description

list_releases

List releases in a repo (filter by draft, prerelease, page/limit)

get_release

Get a single release by its numeric id

get_release_by_tag

Get a release by its tag name (e.g. v1.2.0)

create_release

Create a release with tag_name, name (title), body (notes), target_commitish, draft, prerelease

update_release

Update a release's title, notes, draft/prerelease flags, or rename the tag

delete_release

Delete a release by id — irreversible

Note: a release is identified by its numeric id (from list_releases or get_release_by_tag), NOT the tag name. create_release and update_release maintain the title (name) and release notes (body). Deleting a release may or may not delete the underlying Git tag depending on the Gitea configuration.

Repository

Tool

Description

update_repo

Edit a repository's metadata — description, name (rename), website, private, default_branch (only provided fields change)

Note: name renames the repository and changes its URL; confirm with the user before renaming. To change the repo description, pass only description (an empty string clears it).

Wiki

Tool

Description

list_wiki_pages

List all wiki pages in a repo (metadata only, page/limit)

get_wiki_page

Get one wiki page by pageName — content returned as plain Markdown (base64 decoded)

create_wiki_page

Create a wiki page (title, plain-Markdown content, optional message)

update_wiki_page

Edit a wiki page by pageName; pass title to rename (only provided fields change)

delete_wiki_page

Delete a wiki page by pageName — recoverable only from the wiki git clone

list_wiki_revisions

List the revision history of one wiki page by pageName

Note: pageName is the page title as it appears in the wiki URL (e.g. Home, Getting-Started). Home is the landing page; _Sidebar and _Footer are the layout pages. Page content is always plain Markdown — the tools handle the API's base64 encoding for you. Requires the repo's wiki feature to be enabled (404 otherwise).

Projects (Placeholder)

Tool

Description

list_projects

List project boards (kanban) — placeholder: always returns [] (Gitea has no projects REST API yet)

get_project

Get a project board by id — placeholder: always reports not-found (Gitea has no projects REST API yet)

Note: These tools exist as a stable contract so planning workflows can ask about projects without breaking. They make no HTTP request and always return empty / not-found results. When the upstream Gitea projects REST API lands (go-gitea/gitea#36824), real HTTP calls will be wired in transparently.

Repository Helpers

Tool

Description

list_my_repos

List repositories accessible to the authenticated user

resolve_repo

Detect baseUrl, owner, and repo from the project's git remotes (upstream preferred, then origin); echoed remote URLs are stripped of any embedded userinfo

gitea_status

Inspect credential-handling state — active candidate, exhausted candidates, last error (redacted; secrets never exposed)

configure_gitea

Configure the Gitea connection at runtime (session-scoped, never persisted). Accepts base_url, owner, repo, and/or username. Providing base_url or username triggers credential re-discovery from the existing local sources — tokens never pass through this tool

AI Guidance & Skills

The server ships guidance so assistants use the tools correctly and safely, through three channels:

  • instructions (on connect) — a concise strategy the server sends during the MCP handshake; capable clients inject it into the system prompt automatically.

  • Tool descriptions — every tool's description flags its key risk (pagination, label ID-vs-name, destructive scope) and a minimal usage example.

  • Prompts & resources — workflow templates (triage_issues, summarize_issue, triage_pull_requests, summarize_pull_request, audit_labels, milestone_report, triage_action_runs) and on-demand reference docs (field reference, label guide, tool cookbook) for clients that surface them.

Action skills

For opencode and other tools, the server ships a set of action-scoped skills — one per workflow, so the assistant loads only the guidance it needs (and never, say, delete instructions while creating). Install them with the gitea-mcp init --tool <name> command shown above.

Skill

Invoke when

gitea-find-issues

discovering / reading / triaging issues

gitea-update-issue

editing fields, closing, clearing assignee/milestone

gitea-label-issue

adding / replacing / removing / clearing labels on an issue

gitea-manage-labels

creating or editing label definitions

gitea-comment-issue

posting a comment that advances an issue's discussion

gitea-summarize-issue

reading and summarizing an issue's discussion

gitea-plan-milestones

creating / editing / closing milestones

gitea-resolve-repo

resolving owner/repo or listing repositories

gitea-configure

fixing the connection — instance URL, token, or 401/403 errors

gitea-find-pulls

discovering / reading pull requests, their commits and files

gitea-create-pull

creating a pull request (after a duplicate check)

gitea-update-pull

editing fields, closing without merging, reopening, WIP toggle

gitea-merge-pull

merging a pull request (after mergeability check + user confirmation)

gitea-summarize-pull

reading and summarizing a pull request for review

gitea-find-actions

discovering / reading Actions workflow runs

gitea-cancel-action

cancelling an active run (after status check + user confirmation)

gitea-rerun-action

rerunning a completed run — full or failed jobs only

gitea-write-wiki

writing / editing wiki pages per the bundled OSS wiki format spec

Each skill is a short, AI-facing action flow (purpose, when to use, when not to, rules, and what to check first). The comment and milestone skills also embed body templates (comment, milestone) that standardize the format of what the assistant writes. Destructive single-tool actions (delete issue / comment / label / milestone) are intentionally left to the tool descriptions so they never contaminate a creative workflow.

Development

git clone https://github.com/amonstack/gitea_mcp.git
cd gitea_mcp
npm ci

Command

Description

make lint

Type-check only

make build

Compile src/ to dist/

make test

Run unit tests

make test-watch

Run tests in watch mode

make test-integration

Run integration tests (needs live Gitea instance)

make scan

Scan for leaked secrets with gitleaks (part of make verify)

make verify

Full CI gate: install, secret scan, lint, build, unit tests, smoke run

make dev

Run directly with tsx

For the full architecture — module layout, dependency graph, core patterns, and the guide to adding a new tool — see docs/architecture.md.

Contributing

Contributions are welcome! Please read CONTRIBUTING.md for guidelines on reporting bugs, suggesting features, and submitting pull requests.

License

MIT — Copyright (c) 2026 amonstack.

Available Tools

72 tools
add_issue_blockA

Make an issue block another issue. index is the blocker; dep_index becomes blocked by (dependent on) index. dep_owner/dep_repo default to the same repo but may point at another repo (requires AllowCrossRepositoryDependencies). Returns the blocker issue.

Inputs: index (required), dep_index (required), repo, owner, +2 more.

add_issue_dependencyA

Make an issue depend on (be blocked by) another issue. index is the issue that gains the dependency; dep_index is the blocker. dep_owner/dep_repo default to the same repo but may point at another repo (requires the instance to enable AllowCrossRepositoryDependencies). Returns the target issue (the dependent one).

Inputs: index (required), dep_index (required), repo, owner, +2 more.

add_issue_labelsA

Add labels to an issue by its index. labels is an array of label NAMES (strings) — NOT ids. Get valid names from list_labels; a name that does not exist errors (404). ADDITIVE: existing labels are kept. Duplicate names (both within the input and against labels already on the issue) are filtered out automatically — only labels not yet present are added.

Inputs: index (required), labels (required), repo, owner.

add_topicA

Add ONE topic to a repository by name. Idempotent: adding an existing topic does not error. Topic name: lowercase letters, digits, and hyphens, starting with a letter/digit. To add several topics at once or to set the exact desired set, prefer replace_topics.

Inputs: topic (required), repo, owner.

cancel_action_runA

Cancel one Actions workflow run by runId. Only valid on runs that are still ACTIVE (status: queued, waiting, in_progress, running, pending) — cancelling an already-completed run returns an error. PARTIALLY DESTRUCTIVE: active jobs are killed and their partial results are discarded. ALWAYS call get_action_run first to confirm the run is still active, and confirm the runId with the user before cancelling. The run's conclusion becomes 'cancelled' after a successful cancel.

Inputs: runId (required), repo, owner.

check_issue_blockedA

Check whether an issue is blocked — i.e. has at least one dependency that is not yet closed (state !== "closed"). Returns a single structured verdict: blocked, blockers (the open dependency issues), total_dependencies, and open_blockers. A convenience aggregator over list_issue_dependencies that paginates internally (no page/limit input). Returns 404 if the repo has not enabled issue dependencies (enable_issue_dependencies).

Inputs: index (required), repo, owner.

clear_issue_labelsA

Remove ALL labels from an issue by its index. Destructive for that issue's labels. Confirm with the user first; if a known subset must remain, use replace_issue_labels with that subset instead.

Inputs: index (required), repo, owner.

configure_giteaA

Configure the Gitea connection at runtime (session-scoped, never persisted). Accepts base_url, owner, repo, and/or username — at least one is required. Providing base_url or username triggers credential re-discovery from the existing four sources (GITEA_REPO_URL repo URL, git config [gitea] token, GITEA_TOKEN env, git credential helpers). Tokens never pass through this tool; they are always read from the local credential sources. username strictly narrows the git credential lookup to that identity (no fallback to other identities). Use this when the server started unconfigured or when you need to switch instances/identities mid-session.

Inputs: repo, owner, base_url, username.

create_commentA

Add a comment to an issue by its index. body is required and supports Markdown. Returns the comment including its id — retain it to later update_comment/delete_comment that exact comment.

Inputs: body (required), index (required), repo, owner.

create_issueA

Create an issue. title is required. labels takes label IDs (numbers) — call list_labels first to map names to IDs, or add labels by name after creation via add_issue_labels. assignees is an array of usernames. Returns the created issue including its number.

Inputs: title (required), body, repo, owner, +4 more.

create_issue_attachmentA

Upload a local file as an attachment on an issue by its index (multipart/form-data). file_path reads a LOCAL file from the machine running gitea-mcp — confirm with the user before uploading any file. The path is confined: it must resolve inside the server's working directory (or GITEA_UPLOAD_ROOT), pass a filename-extension allow-list, avoid sensitive locations (.git, .env*, keys), and stay under a 50 MiB cap. Optional name overrides the stored filename (defaults to the file's basename). Returns the created attachment including its id. RISK: instances can disable attachments (404) and cap upload size (oversized files fail 413/422).

Inputs: index (required), file_path (required), name, repo, +1 more.

create_issue_comment_attachmentA

Upload a local file as an attachment on one issue COMMENT by its comment_id (get the id from list_comments; multipart/form-data, same shape and rules as create_issue_attachment). file_path reads a LOCAL file from the machine running gitea-mcp — confirm with the user before uploading any file. The path is confined the same way (upload root, extension allow-list, sensitive locations, 50 MiB cap). Optional name overrides the stored filename (defaults to the file's basename). Returns the created attachment including its id. RISK: instances can disable attachments (404) and cap upload size (oversized files fail 413/422).

Inputs: file_path (required), comment_id (required), name, repo, +1 more.

create_labelA

Create a label. name required and MUST be unique in the repo (a duplicate -> conflict error). color is 6-digit hex, with or without a leading '#' (e.g. '#ff0000' or 'ff0000'). description optional. Returns the label with its id.

Inputs: name (required), color (required), repo, owner, +1 more.

create_milestoneA

Create a milestone. title required. description optional. due_on optional ISO 8601 (e.g. '2025-12-31T23:59:59Z'). New milestones start in state 'open'. Returns the milestone with its id.

Inputs: title (required), repo, owner, due_on, +1 more.

create_pull_requestA

Create a pull request. title, head (source branch), and base (target branch) are required. For cross-fork PRs use 'owner:branch' in head. labels takes label IDs (numbers) — call list_labels first. Prefix the title with WIP: or [WIP] to prevent accidental merge while work is in progress. Returns the created PR including its number.

Inputs: base (required), head (required), title (required), body, +6 more.

create_releaseA

Create a release. tag_name is required (the Git tag, e.g. 'v1.2.0'); the tag is created if it does not exist. name is the human-readable title; body is the release notes (Markdown). target_commitish (branch or SHA) controls where the tag points (defaults to the repo's default branch). draft and prerelease are optional booleans. Returns the created release including its numeric id.

Inputs: tag_name (required), body, name, repo, +4 more.

create_wiki_pageA

Create a wiki page. title is required ('Home' is the wiki landing page; '_Sidebar' / '_Footer' are the layout pages). content is plain Markdown — base64 encoding is handled for you. message is an optional commit message. RISK: fails if a page with that title already exists — call list_wiki_pages or get_wiki_page first and use update_wiki_page for existing pages. Returns the created page with decoded content.

Inputs: title (required), content (required), repo, owner, +1 more.

delete_commentA

Delete a comment by its id (NOT the issue index — get the id from list_comments). IRREVERSIBLE. Confirm with the user first; only the author or a repo admin may delete (403 otherwise).

Inputs: id (required), repo, owner.

delete_issueA

PERMANENTLY delete an issue by index. IRREVERSIBLE (no recycle bin) and may fail if the instance disallows deletion. Confirm the index with the user first; prefer update_issue({ state: 'closed' }) to close instead of delete.

Inputs: index (required), repo, owner.

delete_issue_attachmentA

Delete one issue attachment by attachment_id (get the id from list_issue_attachments). IRREVERSIBLE. Confirm the attachment id with the user first.

Inputs: index (required), attachment_id (required), repo, owner.

delete_labelA

Permanently delete a label by id (number — NOT the name). IRREVERSIBLE and also removes it from EVERY issue that currently has it. Get the id from list_labels and confirm with the user before deleting.

Inputs: id (required), repo, owner.

delete_milestoneA

Permanently delete a milestone by id. IRREVERSIBLE. Issues assigned to it are NOT deleted — they keep existing but lose the milestone assignment (milestone becomes null). Confirm with the user first; prefer update_milestone({ state: 'closed' }) to preserve history.

Inputs: id (required), repo, owner.

delete_releaseA

PERMANENTLY delete a release by its numeric id (NOT the tag name). IRREVERSIBLE. Depending on Gitea configuration the underlying Git tag may or may not be deleted too. Confirm the id with the user first; prefer update_release({ draft: true }) to unpublish without deleting.

Inputs: id (required), repo, owner.

delete_wiki_pageA

PERMANENTLY delete a wiki page by pageName. The page is removed from the wiki; its history survives only in the wiki git repository (clone .wiki.git to recover), so treat this as IRREVERSIBLE from the API. Confirm the pageName with the user first; prefer update_wiki_page to blank or redirect a page instead of deleting it.

Inputs: pageName (required), repo, owner.

edit_issue_attachmentA

Edit one issue attachment by attachment_id — renames it (name is the new filename). Get the id from list_issue_attachments.

Inputs: name (required), index (required), attachment_id (required), repo, +1 more.

get_action_runA

Fetch one Actions workflow run by its runId (the numeric run ID from list_action_runs or the Gitea web UI — NOT the workflow name or index). Returns the full run including status, conclusion, head_branch, head_sha, event, started_at, completed_at, and actor. Call this BEFORE cancel_action_run (to verify the run is still active) or rerun_action_run (to verify it has completed and is rerunnable).

Inputs: runId (required), repo, owner.

get_issueA

Fetch one issue by its index — the number shown in the issue URL (e.g. #42), NOT the internal id. Use to read the full body, labels, assignee, or milestone of a single issue.

Inputs: index (required), repo, owner.

get_issue_attachmentA

Fetch one issue attachment's metadata by attachment_id (get the id from list_issue_attachments). Returns id, name, size, download_count, browser_download_url — it does NOT download the file content.

Inputs: index (required), attachment_id (required), repo, owner.

get_milestoneA

Fetch one milestone by id (the internal id from list_milestones, NOT the title). Returns progress counts (open_issues / closed_issues), description, state, and due_on.

Inputs: id (required), repo, owner.

get_projectA

Fetch one repository project board (kanban) by id. PLACEHOLDER: always reports not-found (HTTP 404 semantics) because Gitea does not yet ship a REST API for project boards (go-gitea/gitea#36824). No HTTP request is made. The contract is stable so workflows can attempt to look up a project without breaking; the real endpoint will be wired transparently once the upstream API lands.

Inputs: id (required), repo, owner.

get_pull_requestA

Fetch one pull request by its index — the number shown in the PR URL (e.g. #42), NOT the internal id. Returns the full PR including base/head branches, mergeable status, merged flag, labels, and milestone.

Inputs: index (required), repo, owner.

get_releaseA

Fetch one release by its numeric id (NOT the tag name — get the id from list_releases). Returns the full release including the title (name), release notes (body), tag_name, draft/prerelease flags, target_commitish, and attachments. Use get_release_by_tag to look up a release when you only know the tag name.

Inputs: id (required), repo, owner.

get_release_by_tagA

Fetch one release by its tag name (e.g. 'v1.2.0'). Useful when you only know the Git tag, not the numeric release id. Returns the same release shape as get_release; the response includes the id needed for update_release/delete_release.

Inputs: tag (required), repo, owner.

get_wiki_pageA

Fetch one wiki page by its pageName — the page title as it appears in the wiki URL (e.g. 'Home', 'Getting-Started'). Returns the full page with content DECODED to plain Markdown (the API's base64 is handled for you), plus footer, sidebar, commit_count, and last_commit. Use list_wiki_pages first if you are unsure of the exact page name.

Inputs: pageName (required), repo, owner.

gitea_statusA

Report the resolved connection and credential state: whether the server is configured, the current baseUrl, every discovered credential candidate (source, schemes, status, masked username, secretPresent boolean), whether the git binary could be used for credential discovery (gitAvailable — false means only GITEA_REPO_URL / GITEA_TOKEN env or anonymous mode remain: install git or set GITEA_REPO_URL / GITEA_TOKEN), and the session-scoped target (owner, repo, username). Secrets are NEVER returned. Use this when a tool returns 401/403 or NotConfiguredError to diagnose the connection state. Takes no input.

Inputs: no named parameters.

is_pull_mergedA

Check whether a pull request has been merged. Returns a boolean (true = merged, false = not merged). Call before merge_pull_request to avoid a redundant attempt, or to confirm a PR's final state.

Inputs: index (required), repo, owner.

list_action_runsA

List Gitea Actions workflow runs in one repository. Paginated: page is 1-based, limit <= 100; keep paging until a page returns fewer than limit. Filters: branch, event (push, pull_request, schedule, etc.), status (pending, queued, waiting, in_progress, running, success, failure, skipped, cancelled), actor (username that triggered the run), head_sha. The response is a wrapper object { workflow_runs: [...], count: number } — the runs live under the workflow_runs key, NOT at the top level. Use this to find a run's id before calling get_action_run, cancel_action_run, or rerun_action_run.

Inputs: page, repo, actor, event, +5 more.

list_commentsA

List comments on one issue by its index. RISK: returns only the server's DEFAULT first page (this tool exposes no pagination), so long threads may be TRUNCATED — do not assume a short list means few comments. Gitea returns oldest-first. Each comment has an id (used to update/delete it), body (Markdown), user, and timestamps.

Inputs: index (required), repo, owner.

list_issue_attachmentsA

List the attachments on one issue by its index. Each attachment has id (used by get/edit/delete_issue_attachment), name, size, download_count, and browser_download_url. RISK: instances can disable attachments (404 means the feature is off).

Inputs: index (required), repo, owner.

list_issue_blocksA

List the issues that are BLOCKED BY this issue (its 'blocking' dependents). Paginated: page is 1-based, limit <= 100; keep paging until a page returns fewer than limit. Reverse direction of list_issue_dependencies (which lists the issues that block this one).

Inputs: index (required), page, repo, limit, +1 more.

list_issue_dependenciesA

List the issues that BLOCK this issue (its 'blocked by' dependencies). Paginated: page is 1-based, limit <= 100; keep paging until a page returns fewer than limit. Returns 404 if the repo has not enabled issue dependencies (enable_issue_dependencies). Use list_issue_blocks for the reverse direction (issues this issue blocks).

Inputs: index (required), page, repo, limit, +1 more.

list_issuesA

List issues in one Gitea repository. Paginated: page is 1-based, limit <= 100; keep paging until a page returns fewer than limit. Filters: state (default open), labels (comma-separated NAMES). RISK: Gitea may include pull requests here; to list only issues use search_issues with type='issues'. Example: list_issues({ state: 'open', page: 1, limit: 50 })

Inputs: page, repo, limit, owner, +2 more.

list_labelsA

List labels in a repo. Paginated (page 1-based, limit <= 100); page until a page returns fewer than limit. Each label has id (number), name, color (hex), description. ALWAYS call this before any label mutation — label endpoints mix names and ids (add/replace use NAMES, remove/update/delete use IDS).

Inputs: page, repo, limit, owner.

list_milestonesA

List milestones in a repo. Paginated (page 1-based, limit <= 100). RISK: Gitea's DEFAULT returns only OPEN milestones — pass state='all' or 'closed' if you need closed/completed ones. Each milestone has id, title, state, open_issues, closed_issues, due_on.

Inputs: page, repo, limit, owner, +1 more.

list_my_reposA

List repositories the authenticated token's user can access (across ALL owners/orgs). Paginated (page 1-based, limit <= 100). Each repo object is large — keep limit modest. Use to DISCOVER owner/repo values or find where to work, not to list one repo's issues.

Inputs: page, limit.

list_projectsA

List repository project boards (kanban). PLACEHOLDER: always returns an empty list because Gitea does not yet ship a REST API for project boards (go-gitea/gitea#36824). No HTTP request is made. The contract is stable so planning workflows can ask about projects without breaking; wiring the real endpoint will be a transparent change once the upstream API lands.

Inputs: repo, owner.

list_pull_commitsA

List the commits in one pull request by its index. Paginated (page 1-based, limit <= 100). Each entry has sha, html_url, the commit message, and an optional author. Useful for reviewing what a PR changes before merging.

Inputs: index (required), page, repo, limit, +1 more.

list_pull_filesA

List the files changed in one pull request by its index. Paginated (page 1-based, limit <= 100). Each entry has filename, status (added/modified/deleted/renamed), additions, deletions, changes, and html_url. Use to understand a PR's diff scope before reviewing or merging.

Inputs: index (required), page, repo, limit, +1 more.

list_pull_requestsA

List pull requests in one Gitea repository. Paginated: page is 1-based, limit <= 100; keep paging until a page returns fewer than limit. Filters: state (default open), labels (comma-separated NAMES), sort, milestone. Example: list_pull_requests({ state: 'open', page: 1, limit: 50 }). Cross-repo PR search uses search_issues({ type: 'pulls' }).

Inputs: page, repo, sort, limit, +4 more.

list_releasesA

List releases in one Gitea repository. Paginated: page is 1-based, limit <= 100; keep paging until a page returns fewer than limit. Optional filters: draft (true = drafts only, false = published only) and prerelease (true = prereleases only, false = stable only). Each release carries id (used by get_release/update_release/delete_release), tag_name, name (title), body (release notes), draft, prerelease, and attachments. Example: list_releases({ page: 1, limit: 50 })

Inputs: page, repo, draft, limit, +2 more.

list_topicsA

List a repository's topics (tags). Returns the topic name list for the repo — useful to inspect classification before editing. Topic names are lowercase letters, digits, and hyphens. Paginated (page 1-based, limit <= 100).

Inputs: page, repo, limit, owner.

list_wiki_pagesA

List all wiki pages in one Gitea repository (metadata only: title, html_url, sub_url, last_commit — no page content). Paginated: page is 1-based, limit <= 100; keep paging until a page returns fewer than limit. Requires the repo's wiki feature to be enabled (404 otherwise). Example: list_wiki_pages({ page: 1, limit: 50 })

Inputs: page, repo, limit, owner.

list_wiki_revisionsA

List the revision history of one wiki page by pageName (newest first). Returns { commits: [{ sha, message, author, commiter }], count }. Paginated: page is 1-based; keep paging while a page returns a full page of commits. Use to audit who changed a page and when, before reverting or summarizing changes.

Inputs: pageName (required), page, repo, owner.

merge_pull_requestA

Merge a pull request by index. Do selects the strategy: 'merge' (merge commit), 'squash' (single commit), 'rebase' (rebase + fast-forward), 'rebase-merge' (rebase + merge commit). Optional MergeTitleField/MergeMessageField customize the merge commit; SHA guards against branch drift. IRREVERSIBLE — confirm the index, strategy, and that the PR is mergeable (get_pull_request mergeable: true) with the user BEFORE merging. Check is_pull_merged first if unsure.

Inputs: Do (required), index (required), SHA, repo, +3 more.

remove_issue_blockA

Remove a block so index no longer blocks dep_index. dep_owner/dep_repo default to the same repo. Returns the blocker issue. 404 if dependencies are not enabled on the repo.

Inputs: index (required), dep_index (required), repo, owner, +2 more.

remove_issue_dependencyA

Remove a dependency so index is no longer blocked by dep_index. dep_owner/dep_repo default to the same repo. Returns the target issue (the formerly dependent one). 404 if dependencies are not enabled on the repo.

Inputs: index (required), dep_index (required), repo, owner, +2 more.

remove_issue_labelA

Remove ONE label from an issue. Takes the label id (number) — NOT the name. Get the id from list_labels (issue labels carry their id). Errors if the label is not currently on the issue.

Inputs: id (required), index (required), repo, owner.

remove_topicA

Remove ONE topic from a repository by name. No error if the topic is not currently on the repo (idempotent delete). Topic name: lowercase letters, digits, and hyphens, starting with a letter/digit. Confirm with the user first.

Inputs: topic (required), repo, owner.

replace_issue_labelsA

REPLACE the issue's ENTIRE label set. labels is an array of label NAMES. Every existing label is removed and ONLY the listed ones remain. Read current labels first if any must survive; confirm with the user before replacing.

Inputs: index (required), labels (required), repo, owner.

replace_topicsA

REPLACE a repository's ENTIRE topic set. topics is the full list of topic names that should remain after the call — every existing topic not listed is removed. Pass an empty array to clear all topics. Topic names: lowercase letters, digits, and hyphens, starting with a letter/digit. Read current topics with list_topics first if any must survive; confirm with the user before replacing.

Inputs: topics (required), repo, owner.

rerun_action_runA

Rerun an entire Actions workflow run by runId. Only valid on runs that have COMPLETED (status: success, failure, cancelled, skipped) — rerunning an active run returns an error. Requires Gitea 1.26.0+. Creates a NEW run (incrementing run_attempt); the original run is not modified. ALWAYS call get_action_run first to confirm the run has completed, and confirm the runId with the user before rerunning. To rerun ONLY the failed jobs instead of the whole run, use rerun_action_run_failed_jobs.

Inputs: runId (required), repo, owner.

rerun_action_run_failed_jobsA

Rerun ONLY the failed jobs of an Actions workflow run by runId. More efficient than rerun_action_run when most jobs succeeded and only a subset failed. Only valid on completed runs. Requires Gitea 1.26.0+. ALWAYS call get_action_run first to confirm the run has completed and has failed jobs (conclusion: failure), and confirm the runId with the user before rerunning.

Inputs: runId (required), repo, owner.

resolve_repoA

Detect baseUrl/owner/repo from a git repository's remotes (SSH or HTTPS). Reads upstream first, then origin, then any other remote; all discovered remotes are returned, with userinfo stripped from every echoed URL so a credentialed remote never leaks its secret. path defaults to the current directory. Call ONCE at the start of a session to establish owner/repo for later calls instead of guessing. Errors if no parseable remote is found.

Inputs: path.

search_issuesA

Search issues (and pull requests) across ALL repositories the token can see, by keyword/type/state/labels. Use for 'find issues about X' or duplicate detection across repos; set type='issues' to exclude pull requests. For listing one repo's issues use list_issues instead.

Inputs: page, type, limit, query, +2 more.

update_commentA

Edit a comment by its id (NOT the issue index — get the id from list_comments). body is the full replacement Markdown. Only the comment author or a repo admin may edit (403 otherwise).

Inputs: id (required), body (required), repo, owner.

update_issueA

Update one issue by index (PATCH: only provided fields change). RISK: passing labels REPLACES the entire label set (give the full desired ID list) — to change a single label use add_issue_labels/remove_issue_label instead. state is 'open' or 'closed'; set milestone by ID.

Inputs: index (required), body, repo, owner, +6 more.

update_labelA

Update a label by id (number — NOT the name). Provide any of name/color/description (PATCH semantics). color is 6-digit hex. Get the id from list_labels.

Inputs: id (required), name, repo, color, +2 more.

update_milestoneA

Update a milestone by id (PATCH: only provided fields change). Provide any of title/description/due_on/state. state is 'open' or 'closed'. NOTE: closing a milestone does NOT close its open issues (they stay open, merely ungrouped) — close the issues separately if required.

Inputs: id (required), repo, owner, state, +3 more.

update_pull_requestA

Update one pull request by index (PATCH: only provided fields change). Set state to 'closed' to close a PR WITHOUT merging (reopens with 'open'). RISK: passing labels REPLACES the entire label set (give the full desired ID list); base retargets the PR and is rarely reversible. To change a single label use add_issue_labels/remove_issue_label (PR #N == Issue #N — label endpoints are shared).

Inputs: index (required), base, body, repo, +7 more.

update_releaseA

Update one release by numeric id (PATCH: only provided fields change). Edit name (title), body (release notes), toggle draft/prerelease, retarget via target_commitish, or rename the tag via tag_name. tag_name renames the underlying Git tag — use with care. Publish a draft by setting draft=false. Get the id from list_releases or get_release_by_tag first.

Inputs: id (required), body, name, repo, +5 more.

update_repoA

Edit ONE repository's metadata (PATCH: only provided fields change). Provide any of name/description/website/private/default_branch. Use description to change the repo description (pass an empty string to clear it). NOTE: name RENAMES the repo and changes its URL — confirm with the user first. Returns the updated repository.

Inputs: name, repo, owner, private, +3 more.

update_wiki_pageA

Edit an existing wiki page by pageName (PATCH: only provided fields change). content is plain Markdown (base64 handled for you); omit it to keep the current content. RISK: passing title RENAMES the page, which breaks existing links to the old name. Read the page with get_wiki_page first if it may have changed since your last read. Returns the updated page with decoded content.

Inputs: pageName (required), repo, owner, title, +2 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 72 tools

    Disambiguation3/5

    Most tools target distinct resources and actions, but the sheer number creates confusion risk: list_issues can include PRs, get_release vs get_release_by_tag are near-duplicates, and the dependency/block pairs (add_issue_dependency vs add_issue_block) require careful reading. The detailed descriptions help, but some overlap exists.

    Naming Consistency4/5

    The vast majority follow a clear verb_noun snake_case pattern (create_issue, list_releases, update_milestone). Minor deviations like gitea_status, is_pull_merged, and the edit/update and remove/delete alternations keep it from being perfect, but the pattern is largely predictable.

    Tool Count1/5

    72 tools is an extreme agent-facing surface, far beyond the 25+ 'too many' threshold and into the 50+ extreme-mismatch range. Several tools are near-variants (label-set mutations, dependency/block pairs, attachment variants), and two project tools are explicit no-op placeholders.

    Completeness3/5

    Core workflows for issues, PRs, labels, milestones, releases, wiki, and Actions are extensively covered with full CRUD. However, repository lifecycle is incomplete (no create_repo/delete_repo/get_repo), project boards are placeholders, and PR reviews and release attachments are missing.

    Maintenance

    ActivityNo data
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      D
      maintenance
      MCP Tool Server for Gitee, supporting the management of repository files/branches, Issues, and Pull Requests.
      20
      82 npm
      7
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      An MCP server that enables AI assistants to read, create, and manage issues, repositories, and releases on Gitea, Forgejo, or Codeberg instances via personal access tokens.
      21
      129 PyPI
      2
      MIT