forgejo-mcp-rs
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| RUST_LOG | No | Log verbosity; logs go to stderr (stdout is the MCP transport), e.g. RUST_LOG=forgejo_mcp_rs=debug. | |
| FORGEJO_URL | No | Instance base URL (e.g. https://codeberg.org or https://gitea.com). | https://codeberg.org |
| FORGEJO_TOKEN | No | Alternative name for the mandatory read token, used instead of FORGEJO_TOKEN_READ_ONLY. | |
| FORGEJO_FLAVOR | No | forgejo, gitea, or auto to detect from the instance version. Only the Actions (CI) tools consult it. | auto |
| FORGEJO_TOKEN_WRITE | No | Write/delete-scoped token. Providing it enables the write tools (which also require entering write mode); omit it for a pure read-only server. | |
| FORGEJO_UPLOAD_ROOT | No | Directory upload_release_asset may read files from. Uploading is disabled while this is unset. | |
| FORGEJO_MIRROR_TOKEN | No | Credential add_push_mirror sends as the remote's password (e.g. a GitHub PAT). Kept out of the conversation — never passed as a tool argument. Omit if you only use use_ssh=true mirrors. | |
| FORGEJO_MIGRATE_TOKEN | No | Credential migrate_repo sends to the source instance it reads from. Also never passed as a tool argument. Kept separate from FORGEJO_MIRROR_TOKEN on purpose. Omit if you only migrate public repos. | |
| FORGEJO_UPLOAD_MAX_MB | No | Ceiling on one uploaded asset (MiB). The file is read into memory to be sent. | 100 |
| FORGEJO_WRITE_MINUTES | No | Default write-mode window (minutes, max 60). | 10 |
| FORGEJO_TOKEN_READ_ONLY | No | Read token (or FORGEJO_TOKEN). Read-only scopes (read:repository, read:issue, read:user) are enough. A read token is mandatory — the server refuses to start on a write token alone, and the read token must be a different token from FORGEJO_TOKEN_WRITE. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| add_push_mirrorA | Add a push mirror so Forgejo/Codeberg auto-pushes this repo to an external remote (e.g. a GitHub mirror), keeping it in sync without a local |
| cancel_workflow_runA | Cancel a pending or running Actions workflow run (owner/repo/run_id; requires write mode), on Forgejo 16+ or Gitea. Answers with the run as it now stands, in list_workflow_runs' shape — read the outcome from its |
| comment_on_issueA | Post a comment on an issue or pull request (they share one numbering), as the write token's account; requires write mode. The body is Markdown and is public on a public repo: @mentions notify those users, and everyone watching the issue is notified. Not idempotent: calling twice posts twice, so check list_issue_comments before retrying a call whose result you did not see. Answers with the new comment in list_issue_comments' shape (id, user, body, created_at, url). To close, reopen or relabel, use edit_issue; for a pull-request review with a verdict, create_pull_request_review. |
| create_branchA | Create a branch in a repository (owner/repo/new_branch, optional old_ref; requires write mode). Answers with the branch in list_branches' shape: name, head commit and protection. |
| create_issueA | Create an issue in a repository (owner/repo/title, optional body; requires write mode). Answers with the new issue in get_issue's shape; its |
| create_or_update_fileA | Create a file, or replace one's content, as a single commit (owner/repo/path/content; requires write mode). content is the whole file as UTF-8 text. To update, pass the file's current sha from get_file_contents; without sha the file is created, and the forge refuses if it already exists. A stale sha is refused too, so a change made meanwhile is not overwritten. Commits to branch (default: the default branch), or to new_branch created from it — the way to prepare a pull request. Answers with the file's new sha and the commit. |
| create_pull_requestA | Open a pull request in a repository (owner/repo/title/head/base, optional body; requires write mode). Answers with the new pull request in get_pull_request's shape; its |
| create_pull_request_reviewA | Submit a review on a pull request (owner/repo/index/verdict; requires write mode). verdict is approve, request_changes or comment; body is the review's summary, which the forge requires for request_changes and comment. commit_id pins the review to a commit (full SHA); by default it is the current head. A forge will not let you approve your own pull request. Answers with the review in list_pull_request_reviews' shape. |
| create_releaseA | Create a release on a git tag (requires write mode). The tag must already exist unless target_commitish is given, which creates it at that commit. Returns the new release, whose |
| create_repoA | Create a repository under your own account, not an organization (requires write mode; defaults to private). It starts empty, with no commit or branch, so push to it or add a first file with create_or_update_file. Not idempotent: it fails if you already have a repo of that name. Answers with the repository in get_repo's shape. To copy an existing repo in from another instance, with its issues and pull requests, use migrate_repo instead; edit_repo changes settings afterwards. |
| delete_branchA | Delete a branch (owner/repo/branch; requires write mode). The forge refuses the default branch and protected branches. Commits only that branch held become unreachable, so check it is merged first (get_pull_request's |
| delete_fileA | Delete a file as a single commit (owner/repo/path/sha; requires write mode). sha is the file's current blob SHA from get_file_contents; a stale one is refused. Commits to branch (default: the default branch), or to new_branch created from it. Answers with the commit. |
| delete_push_mirrorA | Remove one push mirror from a repository, by its remote_name from list_push_mirrors. Requires write mode. Only the mirror's configuration goes: syncing to that remote stops, and the remote repository and everything already pushed to it are left untouched. Answers {deleted: remote_name}. To stop syncing on every commit but keep the mirror, there is no edit tool; delete it and add it again with add_push_mirror. |
| delete_release_assetA | Delete one asset from a release by attachment_id (requires write mode). Used to replace a same-named asset, which Forgejo would otherwise keep alongside the new one. |
| delete_repoA | Permanently delete a repository with its issues, pull requests, releases, wiki and settings. There is no undo, so have the user confirm the exact repo first. Requires write mode, a write token with admin rights on the repo, and |
| disable_write_modeA | Leave write mode now instead of waiting for it to time out, so write tools are refused again until enable_write_mode. Harmless when write mode is already off. Answers with the instance and write_mode_active=false; tell the user, naming the instance. Call it as soon as a batch of writes is done. |
| dispatch_workflowA | Trigger an Actions workflow via workflow_dispatch (owner/repo/workflow/ref, optional inputs; requires write mode), on Forgejo or Gitea. |
| edit_issueA | Edit an issue in place (owner/repo/index; requires write mode): close or reopen it with state (open/closed), or change its title, body, assignees (logins), milestone (id; 0 removes it), due_date (RFC 3339; empty removes it) or labels (names). Only the fields you pass change; title, body, assignees and labels replace what was there. Labels are matched by exact name, and any the repository has no label for are listed in |
| edit_releaseA | Edit an existing release in place, by release_id (requires write mode): name, body (the release notes), tag_name, target_commitish, draft, prerelease. Only the fields you pass are sent, so omitting one leaves it as it is. Reach for this to correct published release notes — deleting and recreating the release would take its assets with it. Same endpoint and response on Forgejo and Gitea. |
| edit_repoA | Edit repository settings: visibility (private true/false), description, website, default_branch, unit toggles (has_issues, has_pull_requests, has_wiki, has_releases, has_actions, has_packages, has_projects), archived. Only provided fields change; renames are not supported. A unit being off hides its whole endpoint family behind 404s rather than returning empty results, so set has_releases=true or has_actions=true when those tools 404 on a repository that plainly exists. Requires write mode. |
| enable_write_modeA | Enter write mode for a limited time (default 10 min, max 60), required before any write tool. Announce this to the user, naming the instance from the returned |
| get_combined_statusA | The rolled-up CI verdict for a commit, branch or tag (owner/repo/ref): one state — success, pending, failure, error or warning — with the latest state per check behind it. This is the tool for "did CI pass?". get_commit_statuses returns the full transition history instead, which is the wrong shape for that question. state is omitted when the instance reports nothing for the ref. Both forges return target_url relative to the instance origin, so prefix the host before following it. |
| get_commitA | Get one commit (owner/repo/sha), slimmed to the same fields as list_commits. Use it to check whether a commit was signed and whether the forge could verify it: |
| get_commit_statusesA | List the statuses reported against a commit, branch or tag (owner/repo/ref): each check's context, status (success, pending, failure, error, warning), description and target_url. Every transition is recorded, so one ref carries a history rather than a state: the same context appears as 'Waiting to run', then 'In progress', then its result. The newest entry per context is the current verdict — for "did CI pass?" call get_combined_status instead, which returns one state with the latest per check. Neither Forgejo nor Gitea exposes a logs API, so target_url is often the only route to a failing run's detail; both return it relative to the instance origin, so prefix the host before following it. Creator and timestamps beyond updated_at are dropped. |
| get_file_contentsA | Read a file's contents from a repository (owner/repo/path, optional ref); decodes text, lists directories. Optional start_line/end_line take a 1-indexed inclusive window, clamped to the file; |
| get_issueA | Get one issue by number from a repository (owner/repo/index): list_issues' fields plus body, locked, due_date and ref. Issues and pull requests share one numbering, so a pull request's number works here too and is marked |
| get_job_logA | Read part of one Actions job's log (owner/repo/job_id — the job |
| get_pull_requestA | Get one pull request by number from a repository (owner/repo/index): list_pull_requests' fields plus body, merged_by, merge_commit_sha, merge_base, assignees, requested_reviewers, milestone, comment counts and diff_url. head_sha is what list_workflow_runs takes to find its CI. For the changes themselves use list_pull_request_files or get_pull_request_diff. |
| get_pull_request_diffA | Read a pull request's unified diff (owner/repo/index). Pass file_path for just that file's hunks — the exact path from list_pull_request_files, matching either side of a rename. Without it the whole diff is returned, truncated at 64 KiB (raise with max_bytes). |
| get_releaseA | Get one release by its git tag (owner/repo/tag). Returns the release's identity, notes ( |
| get_repoA | Get one repository's details (owner/repo): default branch, size (KiB), and the unit toggles (has_issues, has_pull_requests, has_wiki, has_releases, has_actions, has_packages, has_projects). A unit that is off 404s its whole endpoint family rather than returning empty, so this is where to check when a release or workflow tool reports a repository that plainly exists as missing. |
| get_repo_treeA | Read a repository's git tree (owner/repo, optional sha — a branch, tag or commit, defaulting to HEAD). With recursive=true the whole tree arrives in one request, which answers "where is every file of this kind" without descending directory by directory; without it, one level. Entries are path, type (blob/tree/commit), size and sha. Check |
| get_workflow_runA | Get one Actions workflow run by run_id (owner/repo/run_id), in list_workflow_runs' shape on both forges: status (the outcome once there is one), event, workflow, ref, commit_sha, html_url and timestamps. For its jobs and logs use list_workflow_jobs and get_job_log. |
| list_branch_protectionsA | List a repository's branch protection rules (owner/repo). Shows what each rule enforces — require_signed_commits, whether direct pushes are allowed, required approvals, status checks — for a branch name or glob. A branch matching no rule is unprotected, so an empty list means nothing is protected rather than a missing feature. require_signed_commits rejects pushes that are unsigned OR unverifiable, and is checked against the commits a push introduces rather than the branch's history, so enabling it does not invalidate what is already there. Allowlists of usernames, teams and deploy keys are omitted: they answer who may bypass a gate, not what is enforced. enable_force_push is Gitea-only and apply_to_admins Forgejo-only — absent means the forge does not report it, not false. |
| list_branchesA | List a repository's branches (owner/repo), most recently pushed first, each slimmed to name, head commit SHA and whether it is protected. Without page/limit it walks every page and returns them all, up to 1,000 with |
| list_commitsA | List commits in a repository (owner/repo), newest first; optional sha (branch, tag or commit to start from; defaults to the default branch) and path to filter by file. Auto-paginated. Slimmed to sha, subject line, author, date and signature state — the raw objects carry base64 signature and payload blobs, plus per-commit files and stats, that dwarf everything worth reading. |
| list_issue_commentsA | List the discussion comments on an issue or pull request (owner/repo/index), oldest first. Each comment is {id, user, body, created_at, url}. Returns the whole thread with |
| list_issuesA | List issues in a repository (owner/repo), open by default; optional state (open/closed/all) and page/limit. Filters: labels and milestones (names; an issue with any of them matches), author, assignee, mentioned (logins), search (text), since/before (RFC 3339, by last update). A label or milestone name the forge does not know is refused rather than ignored, since the forge would then list every issue. sort orders the list (oldest, recentupdate, mostcomment, nearduedate, …) on Forgejo only: Gitea's issue listing has no sort, so there it is refused. Issues only — pull requests, which the forges also serve as issues, are left out; use list_pull_requests for those. Each issue is slimmed to number, title, state, author, labels, assignees, milestone, comment count and dates, without its body; get_issue has the body. |
| list_keysA | List an account's SSH and GPG keys (omit username for the authenticated user). A key must be present AND verified before the instance will verify signatures made with it. IMPORTANT: |
| list_my_reposA | List the repositories your account can work in: your own, plus those of organizations you belong to and those you collaborate on, sorted by name (case-insensitive). Without page/limit it walks every page and returns the whole set, up to 1,000 with |
| list_notificationsA | List your notification threads, most recently updated first: unread only by default, read ones too with all=true. Each is slimmed to id, unread, repo, type (Issue, Pull, Commit or Repository), state, title, url and updated_at, with |
| list_orgsA | List the organizations your account belongs to, as the forge returns them: username (the owner name to pass to other tools), full_name, description, visibility, website and location. One page at a time (page/limit, at most 50), with |
| list_pull_request_filesA | List the files a pull request changes, with per-file additions/deletions and rename info (owner/repo/index; optional page/limit, auto-paginated when both are omitted). Forgejo does not return the hunks here — pass a filename to get_pull_request_diff for the content. |
| list_pull_request_reviewsA | List the reviews on a pull request — approve/request-changes/comment verdicts and their summary bodies (owner/repo/index; optional page/limit). Inline line comments are reported only as a count. |
| list_pull_requestsA | List pull requests in a repository (owner/repo); optional state (open/closed/all), base and head branch, author (login), sort (oldest, recentupdate, recentclose, mostcomment, …; newest first by default), and page/limit. |
| list_push_mirrorsA | List the push mirrors configured on a repository (owner/repo): for each, its remote_name (which delete_push_mirror takes), remote_address, sync interval, whether it syncs on every commit, and when it last synced and with what error, if any. Requires write mode (mirror config is repo-admin-scoped), though it changes nothing. Secrets are never returned. Use it after sync_push_mirrors to see whether the push worked. |
| list_release_assetsA | List the assets (attached files) of one release, by release_id. Returns each asset's id, name, size and download URL; the id is what delete_release_asset takes. |
| list_releasesA | List a repository's releases (owner/repo), newest first, each with its asset_count and asset_names; optional page/limit. For an asset's id, size or download URL use list_release_assets with the release |
| list_workflow_jobsA | List the jobs of one Actions workflow run (owner/repo/run_id), on Forgejo 16+ or Gitea — the step between a failed run and reading the log of the job that failed it. Each job's |
| list_workflow_runsA | List a repository's Actions (CI) workflow runs (owner/repo), on Forgejo or Gitea. Newest first, 30 to a page unless page/limit say otherwise; |
| merge_pull_requestA | Merge a pull request (owner/repo/index; requires write mode). style is merge (default), rebase, rebase-merge, squash or fast-forward-only, as the repository allows; title and message set the merge commit's; delete_branch removes the head branch afterwards. Pass head_sha (get_pull_request's, in full) to merge only if nothing was pushed since you looked. The forge refuses a pull request that is not mergeable — conflicts, failing required checks or missing approvals — and says why. Answers with the pull request as it now stands, in get_pull_request's shape. |
| migrate_repoA | Migrate (copy) a repository from ANOTHER forge/instance into this one — the only way to move issues and PRs across instances, unlike push mirrors which carry git refs only. Give clone_addr (http/https URL on the source) and repo_name. Set service to the source forge ("gitea" for Forgejo; the default "git" copies refs only) and turn on issues/pull_requests/labels/milestones/releases/wiki, which all default to false. The import runs within the call, which answers with the finished repository (seconds for a small one). A failure can leave an empty repository under repo_name; the error says so, and it must be deleted before retrying under that name. This copies — the source repo is left untouched. Requires write mode. For a private source, the credential comes from the server's FORGEJO_MIGRATE_TOKEN env var — never pass it as an argument. Set authenticate=true to send it; auth_username is only for service git (optional), onedev and codebase, and is refused for gitea, github, gitlab, gogs and gitbucket, whose importers ignore the token when given a username. |
| rerun_workflowA | Rerun a finished Actions workflow run (owner/repo/run_id; requires write mode), or with job_id only that job and the jobs that depend on it — the job |
| search_reposA | Find repositories anywhere on the instance by keyword: a case-insensitive substring of the repo name or one of its topics (include_description=true widens it to descriptions; owner names are not searched). Covers every repo this token can see, private ones included. Results come alphabetically by name, not ranked by relevance, one page at a time (set limit, up to 50, and page), in list_my_repos' slim shape (full_name, description, private, stars, size, urls, dates), with |
| sync_push_mirrorsA | Push a repository (owner/repo) to all of its push mirrors now, instead of waiting for their interval. Requires write mode. A mirror push makes each remote match this repository, so branches and tags that exist only on the remote are overwritten or deleted; that is why it is marked destructive. Answers {sync_requested: "owner/repo"} once the forge has queued the sync; the push itself runs in the background, so check each mirror's last sync time and error with list_push_mirrors afterwards. |
| upload_release_assetA | Upload a local file as a release asset (requires write mode). Give release_id and file_path; the published name defaults to the file's own. The file must resolve inside the server's FORGEJO_UPLOAD_ROOT — uploads are refused outright when that is unset, since whatever is read becomes publicly downloadable. Forgejo keeps same-named assets side by side, so delete the old one first when replacing. |
| versionA | Report this MCP server's name and version, the connected instance's URL and version, and whether it is Forgejo or Gitea ( |
| whoamiA | Report the account this server's token belongs to, as the forge returns it: login (the owner name for your own repos), full_name, email, is_admin, visibility, created and last_login among others. A cheap first call: it proves the token works, and if it fails, the token or FORGEJO_URL is wrong and no other tool will work either. Use version for the server and forge versions, and write_status for whether writes are possible. |
| write_statusA | Report write-mode status: which instance this server writes to, whether a write token is configured, whether write mode is active, and minutes remaining. Changes nothing and does not contact the forge. Call it before a write to learn whether enable_write_mode is needed, or whether no write token is configured, in which case enabling cannot help and the user has to set FORGEJO_TOKEN_WRITE. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 59 tools
Each tool targets a distinct resource and action, and overlapping areas are explicitly disambiguated by the descriptions (e.g. get_combined_status vs get_commit_statuses, get_file_contents vs get_repo_tree, list_issues vs list_pull_requests). The cross-references between related tools make misselection unlikely despite the large surface.
The set predominantly follows a verb_noun snake_case convention (list_repos, get_issue, create_pull_request, delete_branch), which is predictable. Minor deviations like version, whoami, write_status, and enable_write_mode/disable_write_mode keep it from being perfect but do not impede understanding.
With 59 tools, the server is far above the practical 3-15 tool range and presents a heavy surface for an agent to navigate. The breadth of Forgejo's API partially justifies the scope, but the count is still excessive for reliable tool selection.
Core lifecycle coverage is strong across repositories, branches, files, commits, issues, pull requests, releases, assets, workflows, notifications, organizations, keys, and mirrors. Minor gaps remain, such as dedicated label/milestone CRUD, tag listing, webhook/team management, and comment editing/deletion, but no major workflow is blocked.