forgejo-mcp
Provides integration with Codeberg as a Forgejo/Gitea instance, enabling management of repositories, files, branches, issues, pull requests, releases, and CI through the same API.
Provides tools for managing repositories, files, branches, issues, pull requests, releases, and CI/Actions on Forgejo instances via the /api/v1 REST API.
Provides tools for managing repositories, files, branches, issues, pull requests, releases, and CI/Actions on Gitea instances via the shared /api/v1 REST API.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@forgejo-mcp list the open pull requests on my Forgejo repo, and show the failing CI runs"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
forgejo-mcp
An MCP server for Gitea and Forgejo: manage repositories, files, branches, issues, pull requests, releases and CI from Claude, much like the GitHub MCP server does for GitHub. It speaks the /api/v1 REST API shared by Forgejo and Gitea (including Codeberg and self-hosted/private instances).
The default and recommended way to run it is locally over stdio. Your token stays on your machine and is passed only to your Forgejo instance. A Cloudflare Worker entry point also exists for hosted use (see the end), but you don't need it.
Quick start (local)
git clone https://github.com/jerdog/forgejo-mcp && cd forgejo-mcp
npm install # also builds dist/ via the `prepare` scriptVerify the connection and token permissions before wiring it into Claude:
export FORGEJO_URL=https://git.example.com
export FORGEJO_TOKEN=... # see "Creating a Forgejo access token"
node dist/stdio.js --check --repo owner/name # --repo is optional--check is read-only. It reports PASS / FAIL / SKIP per probe (reachability, authentication, organizations, repository search, then repo, contents, branches, commits, issues, pull requests, labels, releases and Actions for the repo you name). A 403 names the scope that is probably missing. It never prints the token.
Claude Code
claude mcp add forgejo \
-e FORGEJO_URL=https://git.example.com \
-e FORGEJO_TOKEN=... \
-e FORGEJO_READ_ONLY=true \
-- node /absolute/path/to/forgejo-mcp/dist/stdio.jsOr commit a project .mcp.json (see .mcp.json.example; it reads FORGEJO_URL / FORGEJO_TOKEN from your shell so no secret is committed).
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"forgejo": {
"command": "node",
"args": ["/absolute/path/to/forgejo-mcp/dist/stdio.js"],
"env": {
"FORGEJO_URL": "https://git.example.com",
"FORGEJO_TOKEN": "...",
"FORGEJO_READ_ONLY": "true"
}
}
}
}Related MCP server: gitea-mcp
Claude Code cloud environments
To have the tools in every session of a Claude Code cloud (web) environment, register the server from the environment's setup script. That covers every repository opened in the environment, so nothing is added to those repos.
In the session title bar open the cloud environment menu, choose Edit, and paste this into Setup script:
#!/bin/bash # Cloud environment "Setup script": installs forgejo-mcp and registers it with Claude Code. export FORGEJO_URL=https://git.ontheedgefarms.com REF=main # branch or tag of jerdog/forgejo-mcp to install REPO=jerdog/forgejo-mcp DIR=/opt/forgejo-mcp log() { echo "[forgejo-mcp] $*"; } fetch() { rm -rf "$DIR" log "try 1: git clone via github.com" git clone -q --depth 1 --branch "$REF" "https://github.com/$REPO" "$DIR" && { log "ok: git clone"; return 0; } rm -rf "$DIR"; mkdir -p "$DIR" log "try 2: tarball via api.github.com" curl -fsSL "https://api.github.com/repos/$REPO/tarball/$REF" | tar -xz --strip-components=1 -C "$DIR" \ && [ -f "$DIR/package.json" ] && { log "ok: tarball"; return 0; } if [ -n "${FORGEJO_MCP_GH_TOKEN:-}" ]; then rm -rf "$DIR" log "try 3: git clone with FORGEJO_MCP_GH_TOKEN" git clone -q --depth 1 --branch "$REF" "https://x-access-token:${FORGEJO_MCP_GH_TOKEN}@github.com/$REPO" "$DIR" \ && git -C "$DIR" remote set-url origin "https://github.com/$REPO" && { log "ok: token clone"; return 0; } fi return 1 } fetch || { log "FAILED: could not fetch $REPO@$REF by any route"; exit 1; } bash "$DIR/scripts/cloud-setup.sh" || { log "FAILED: cloud-setup.sh exited $?"; exit 1; } log "done"Private repo: the setup phase has no session GitHub credentials. If you configured a network secret (Bearer) for GitHub, the proxy supplies auth and try 1 or 2 succeeds; list both
github.comand*.github.comin its allowed domains, since a wildcard may not match the bare host. Otherwise add a fine-grained token (Contents: Read-only, this repo only) as environment variableFORGEJO_MCP_GH_TOKENfor try 3. Once the repo is public none of this is needed.scripts/cloud-setup.shbuilds the server, runsclaude mcp add forgejo --scope user, then runs--checkas an informational self-test. A 401 there is expected when the environment injects credentials only inside sessions, so it only warns; confirm authentication from within a session.Make sure Network access allows
github.com(to clone) and your Forgejo host.Start a new session. MCP servers are read at session start, so existing sessions don't pick it up.
Notes:
No token needed if the environment's proxy injects one. If the environment has a credential configured for your Forgejo host (shown as "inject FORGEJO_TOKEN" in the session's proxy notes), the proxy adds the
Authorizationheader to requests it sees, and you can leaveFORGEJO_TOKENunset.NODE_USE_ENV_PROXY=1is required. Node'sfetchignoresHTTPS_PROXYby default (needs Node 22.21+ or 24+), so without it requests bypass the proxy, no credential is injected, and you get401. The script sets it on the server. Proxy CA trust (NODE_EXTRA_CA_CERTS) is already provided by the environment.If the proxy does not inject a token, set
FORGEJO_TOKENas an environment secret and export it before thebash /opt/forgejo-mcp/scripts/cloud-setup.shline, or create a token as described above.Writes are off by default (
FORGEJO_READ_ONLY=true). ExportFORGEJO_READ_ONLY=falsebefore that line to enable write tools; see Read-only mode.Verify in a new session with
/mcp, or runNODE_USE_ENV_PROXY=1 FORGEJO_URL=... node /opt/forgejo-mcp/dist/stdio.js --check --repo owner/name.Alternatively commit a project
.mcp.jsonto a repo (see.mcp.json.example, adding"NODE_USE_ENV_PROXY": "1"toenv) if you only want the tools in that repo; you still need the setup script step to install the server.
Configuration
Variable | Description |
| Base URL of the instance, e.g. |
| Personal access token. Required for private repos and any write |
|
|
Read-only mode
Set FORGEJO_READ_ONLY=true to hide every write tool: the server doesn't register them, so the model can't see or call them (no create_*, update_*, delete_*, merge_*, push_files, dispatch_workflow, and so on). Only the read tools remain. true, 1 or yes (case-insensitive) turn it on; anything else, or leaving it unset, exposes write tools. The stdio server defaults to writes enabled when the variable is unset, which is why every example here sets it explicitly.
It is a server-side switch, not a Forgejo permission. A token with write scopes is still able to write through anything else that uses it; read-only only limits what this MCP server offers. For a hard limit, also give the token read-only scopes (see below), or rely on a read-only credential.
Changes apply at the next session or restart. Tools are registered when the server starts, so restart the client (or start a new cloud session) after changing the variable.
Verify with a tool listing: in read-only mode there is no
create_issueormerge_pull_request.
How to flip it per setup:
Setup | Read-only | Enable writes |
|
| drop the flag, or |
|
| remove the key or set |
Cloud environment | leave the setup script as is ( | add |
Cloudflare Worker | set | set it to |
In a cloud environment where the proxy injects the credential, that credential's permissions apply to writes, and it may be an account with broad rights. Consider enabling writes only in a separate environment, or only for the session that needs them.
Creating a Forgejo access token
In Forgejo/Gitea: Settings → Applications → Manage Access Tokens → Generate Token. Give it a name and select scopes (each category has No access / Read / Read and Write).
To use these tools | Scope |
Read repos, files, branches, commits, tags, releases, pull requests, Actions runs, commit status |
|
Create/update/delete files, branches, releases, pull requests; merge; fork/create repos; dispatch workflows |
|
Read issues, comments, labels, milestones |
|
Create/edit issues, comments, labels |
|
|
|
|
|
Create repos under an organization |
|
Recommended first token: read:repository, read:issue, read:user, read:organization with FORGEJO_READ_ONLY=true. Add write:* scopes only when you want write tools, and drop FORGEJO_READ_ONLY at the same time.
Notes:
Scopes don't grant access on their own: the token only sees repositories its owner can see (membership, team or collaborator access).
Use
--checkto confirm the scopes are enough; a403result names the scope to add.Treat the token like a password: don't commit it, and revoke it in the same settings page if it leaks.
Tools
Users/orgs:
get_me,search_users,list_organizationsRepos:
search_repositories,get_repository,list_repositories,create_repository,fork_repositoryFiles:
get_file_contents,create_or_update_file,delete_file,push_filesBranches/commits:
list_branches,create_branch,delete_branch,list_commits,get_commit,list_tagsIssues:
list_issues,search_issues,get_issue,create_issue,update_issue,list_issue_comments,add_issue_comment,list_labels,create_label,add_issue_labels,remove_issue_label,list_milestonesPull requests:
list_pull_requests,get_pull_request,create_pull_request,update_pull_request,merge_pull_request,get_pull_request_diff,list_pull_request_files,list_pull_request_reviews,list_pull_request_review_comments,create_pull_request_review,request_pull_request_reviewersReleases:
list_releases,get_latest_release,get_release_by_tag,create_releaseCI:
list_action_tasks,get_commit_status,list_workflows,dispatch_workflow(Actions endpoints depend on the instance version)
Write tools carry readOnlyHint: false; deletes and merges set destructiveHint.
Development
npm run typecheck && npm test
npm run smoke -- owner/name # read-only end-to-end run over real MCP stdio (needs FORGEJO_URL/TOKEN)
npx @modelcontextprotocol/inspector node dist/stdio.jsOptional: hosted on Cloudflare Workers
Only needed if you want a remote HTTP endpoint instead of a local process.
# edit FORGEJO_URL in wrangler.toml, then:
npx wrangler secret put FORGEJO_TOKEN
npx wrangler secret put MCP_AUTH_TOKEN # protects the endpoint; required if FORGEJO_TOKEN is set
npm run deployEndpoint: https://<worker>.workers.dev/mcp. Clients send Authorization: Bearer <MCP_AUTH_TOKEN>, or bring their own Forgejo token in X-Forgejo-Token. The Worker must be able to reach your Forgejo URL over the public internet, so it is a poor fit for a private instance. Local dev: npm run dev:worker (vars in .dev.vars).
Available Tools
49 toolsadd_issue_commentComment on issue or pull requestC
Add a comment to an issue or pull request.
Inputs: body (required), repo (required), index (required), owner (required).
add_issue_labelsAdd labels to issueB
Add labels (by ID) to an issue or pull request.
Inputs: repo (required), index (required), owner (required), labels (required).
create_branchCreate branchA
Create a branch from an existing branch (default: the repo's default branch).
Inputs: repo (required), owner (required), branch (required), from_branch.
create_issueCreate issueA
Create an issue. Labels are given as numeric IDs (see list_labels).
Inputs: repo (required), owner (required), title (required), body, +4 more.
create_labelCreate labelC
Create a label in a repository.
Inputs: name (required), repo (required), color (required), owner (required), +1 more.
create_or_update_fileCreate or update fileA
Create a file, or update an existing one (then sha of the current blob is required). Creates a commit.
Inputs: path (required), repo (required), owner (required), content (required), +4 more.
create_pull_requestCreate pull requestC
Open a pull request from head into base. For cross-repo forks use head as owner:branch.
Inputs: base (required), head (required), repo (required), owner (required), +5 more.
create_pull_request_reviewReview pull requestA
Submit a review on a pull request, optionally with inline comments. Omit event to leave the review pending.
Inputs: repo (required), index (required), owner (required), body, +3 more.
create_releaseCreate releaseC
Create a release (and its tag if it does not exist yet).
Inputs: repo (required), owner (required), tag_name (required), body, +4 more.
create_repositoryCreate repositoryB
Create a repository for the authenticated user, or for an organization if org is given.
Inputs: name (required), org, private, auto_init, +2 more.
delete_branchDelete branchCDestructive
Delete a branch.
Inputs: repo (required), owner (required), branch (required).
delete_fileDelete fileCDestructive
Delete a file in a repository via a commit.
Inputs: sha (required), path (required), repo (required), owner (required), +2 more.
dispatch_workflowDispatch workflowC
Trigger a workflow_dispatch run (Forgejo 11+/Gitea 1.24+).
Inputs: ref (required), repo (required), owner (required), workflow (required), +1 more.
fork_repositoryFork repositoryB
Fork a repository to the authenticated user or a given organization.
Inputs: repo (required), owner (required), name, organization.
get_commitGet commitBRead-only
Get a single commit by SHA, including changed files.
Inputs: sha (required), repo (required), owner (required).
get_commit_statusGet combined commit statusARead-only
Get the combined CI status (and individual statuses) for a ref/SHA.
Inputs: ref (required), repo (required), owner (required).
get_file_contentsGet file or directory contentsARead-only
Read a file (decoded to text) or list a directory. Use path '' for the repository root. File results include the blob sha needed to update the file.
Inputs: repo (required), owner (required), ref, path.
get_issueGet issueCRead-only
Get an issue by number.
Inputs: repo (required), index (required), owner (required).
get_latest_releaseGet latest releaseCRead-only
Get the latest published release.
Inputs: repo (required), owner (required).
get_meGet authenticated userARead-only
Get the user the configured token authenticates as. Call this first to verify the connection.
Inputs: no named parameters.
get_pull_requestGet pull requestBRead-only
Get a pull request, including mergeability.
Inputs: repo (required), index (required), owner (required).
get_pull_request_diffGet pull request diffBRead-only
Get the unified diff (or patch) of a pull request.
Inputs: repo (required), index (required), owner (required), format.
get_release_by_tagGet release by tagCRead-only
Get the release for a tag name.
Inputs: tag (required), repo (required), owner (required).
get_repositoryGet repositoryCRead-only
Get details of a repository.
Inputs: repo (required), owner (required).
list_action_tasksList Actions runsBRead-only
List recent Actions (CI) task runs for a repository.
Inputs: repo (required), owner (required), page, limit.
list_branchesList branchesCRead-only
List branches of a repository.
Inputs: repo (required), owner (required), page, limit.
list_commitsList commitsBRead-only
List commits on a branch/ref, optionally limited to a file path.
Inputs: repo (required), owner (required), sha, page, +2 more.
list_issue_commentsList issue commentsBRead-only
List comments on an issue or pull request.
Inputs: repo (required), index (required), owner (required), page, +1 more.
list_issuesList issuesBRead-only
List or search issues in a repository (pull requests excluded).
Inputs: repo (required), owner (required), q, page, +6 more.
list_labelsList labelsBRead-only
List labels of a repository (returns the IDs needed to label issues).
Inputs: repo (required), owner (required), page, limit.
list_milestonesList milestonesCRead-only
List milestones of a repository.
Inputs: repo (required), owner (required), page, limit, +1 more.
list_organizationsList organizationsBRead-only
List organizations the authenticated user belongs to.
Inputs: page, limit.
list_pull_request_filesList pull request filesBRead-only
List files changed by a pull request.
Inputs: repo (required), index (required), owner (required), page, +1 more.
list_pull_request_review_commentsList review commentsBRead-only
List inline comments of a specific review.
Inputs: repo (required), index (required), owner (required), review_id (required).
list_pull_request_reviewsList pull request reviewsCRead-only
List reviews of a pull request.
Inputs: repo (required), index (required), owner (required), page, +1 more.
list_pull_requestsList pull requestsCRead-only
List pull requests in a repository.
Inputs: repo (required), owner (required), page, sort, +3 more.
list_releasesList releasesCRead-only
List releases of a repository.
Inputs: repo (required), owner (required), page, draft, +2 more.
list_repositoriesList repositoriesARead-only
List repositories owned by a user or organization. Omit owner to list the authenticated user's repositories.
Inputs: page, limit, owner, is_org.
list_tagsList tagsBRead-only
List tags of a repository.
Inputs: repo (required), owner (required), page, limit.
list_workflowsList workflowsARead-only
List Actions workflows defined in a repository (Forgejo 11+/Gitea 1.24+).
Inputs: repo (required), owner (required).
merge_pull_requestMerge pull requestCDestructive
Merge a pull request using the chosen strategy.
Inputs: repo (required), index (required), owner (required), title, +3 more.
push_filesPush multiple filesCDestructive
Create, update or delete several files in a single commit.
Inputs: repo (required), files (required), owner (required), message (required), +2 more.
remove_issue_labelRemove label from issueB
Remove one label (by ID) from an issue or pull request.
Inputs: repo (required), index (required), owner (required), label_id (required).
request_pull_request_reviewersRequest reviewersC
Request reviews from users on a pull request.
Inputs: repo (required), index (required), owner (required), reviewers (required).
search_issuesSearch issues and pull requestsCRead-only
Search issues/PRs across all repositories visible to the token.
Inputs: q (required), page, type, limit, +2 more.
search_repositoriesSearch repositoriesBRead-only
Search repositories on the Forgejo/Gitea instance by keyword.
Inputs: query (required), page, limit, topic.
search_usersSearch usersCRead-only
Search users by name.
Inputs: query (required), page, limit.
update_issueUpdate issueB
Edit an issue's title, body, state, assignees or milestone.
Inputs: repo (required), index (required), owner (required), body, +4 more.
update_pull_requestUpdate pull requestC
Edit a pull request's title, body, base branch or state.
Inputs: repo (required), index (required), owner (required), base, +4 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 49 tools
Most tools target a clear resource+action, but several list/search pairs overlap: search_issues vs list_issues, list_pull_requests vs search_issues (PRs are issues in Forgejo), and get_file_contents duplicating listing-directory semantics. Descriptions mostly disambiguate, though an agent could still misselect between list and search variants.
Overwhelmingly consistent verb_noun pattern (list_*, get_*, create_*, update_*, merge_*, add_*, remove_*). Minor deviations like create_or_update_file and dispatch_workflow are still readable and purposeful. The 'list_pull_request_*' long forms are consistent within their family.
49 tools is heavy for a single MCP server and exceeds the 25+ threshold. The surface spans repos, files, branches, commits, issues, PRs, reviews, labels, releases, milestones, and Actions CI—workable but burdened with granular tools that could be consolidated or grouped.
Broad, well-rounded coverage of the Forgejo/Gitea domain: full file CRUD, branches, commits, issues, PRs, reviews, labels, releases, and CI status/workflows. Some gaps remain (e.g., deleting/editing releases, managing milestones, listing PR reviewers/assignees, closing organizations), but core workflows are covered.
Maintenance
Related MCP Connectors
Operate GitHub from your AI assistant: repos, PRs, issues, Actions. Previews and read-only mode.
Manage repositories, users, releases, and automate GitHub workflows
Read and write KukGit repositories, files, issues and pull requests from an AI assistant.
Access the GitHub API, enabling file operations, repository management, search functionality, and…
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables AI assistants to interact with Gitea repositories through intelligent tools for issue/PR management, workflow analysis, compliance checking, and content generation, plus 200+ CLI commands for complete CRUD operations.2281 npm6MIT
- AlicenseNot gradedqualityCmaintenanceA local MCP server that connects Claude directly to your Gitea instance, enabling management of repositories, issues, pull requests, files, and commit history.MIT
- AlicenseCqualityBmaintenanceEnables AI assistants to manage Forgejo repositories, issues, pull requests, files, commits, Actions workflow runs, organizations, and notifications through the Forgejo REST API using natural language.50MIT
- AlicenseBqualityCmaintenanceEnables AI agents to read repositories, manage issues, inspect CI/CD workflow runs, create branches, commit files, and open pull requests on a self-hosted Gitea instance.7MIT