- Tick all 27 tasks.md checkboxes for label-crud change - Add 8 label CRUD tools to README tool table - Add 3 label resource templates to README Resources section - Add resource table to AGENTS.md listing all 12 registered templates - Tick Codeberg #190 acceptance-criteria checkboxes (naming note included) - Close beads issue forgejo-mcp-773
698 lines
32 KiB
Markdown
698 lines
32 KiB
Markdown
# Forgejo MCP Server
|
||
|
||
Connect your AI assistant to Forgejo repositories. Manage issues, pull requests, files, and more through natural language.
|
||
|
||
## What It Does
|
||
|
||
Forgejo MCP Server is an integration plugin that connects Forgejo with [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) systems. Once configured, you can interact with your Forgejo repositories through any MCP-compatible AI assistant like Claude, Cursor, or VS Code extensions.
|
||
|
||
**Example commands you can use:**
|
||
- "List all my repositories"
|
||
- "Create an issue titled 'Bug in login page'"
|
||
- "Show me open pull requests in my-org/my-repo"
|
||
- "Get the contents of README.md from the main branch"
|
||
- "Show me the latest Actions workflow runs in goern/forgejo-mcp"
|
||
|
||
## Quick Start
|
||
|
||
### 1. Install
|
||
|
||
**Option A: Using Go (Recommended)**
|
||
|
||
```bash
|
||
git clone https://codeberg.org/goern/forgejo-mcp.git
|
||
cd forgejo-mcp
|
||
go install .
|
||
```
|
||
|
||
Ensure `$GOPATH/bin` (typically `~/go/bin`) is in your PATH.
|
||
|
||
> **Note:** `go install codeberg.org/goern/forgejo-mcp/v2@latest` does not work currently. See [Known Issues](#known-issues).
|
||
|
||
**Option B: Download Binary**
|
||
|
||
Download the latest release from the [releases page](https://codeberg.org/goern/forgejo-mcp/releases).
|
||
|
||
For Arch Linux, use your favorite AUR helper:
|
||
|
||
```bash
|
||
yay -S forgejo-mcp # builds from source
|
||
yay -S forgejo-mcp-bin # uses pre-built binary
|
||
```
|
||
|
||
**Option C: Nix / NixOS**
|
||
|
||
You can run the server directly using the Nix package manager:
|
||
```bash
|
||
nix-shell -p forgejo-mcp
|
||
```
|
||
Or using Flakes:
|
||
```bash
|
||
nix run nixpkgs#forgejo-mcp
|
||
```
|
||
|
||
> **Note:** `forgejo-mcp` is currently only available in the `unstable` channel and is not yet part of the 25.11 stable release.
|
||
|
||
**Option D: Container image**
|
||
|
||
A signed multi-stage OCI image is published on every release to
|
||
`codeberg.org/goern/forgejo-mcp`. Run the server without building from source:
|
||
|
||
```bash
|
||
# Latest release
|
||
podman run --rm -i \
|
||
-e FORGEJO_ACCESS_TOKEN="<your personal access token>" \
|
||
codeberg.org/goern/forgejo-mcp:latest \
|
||
--transport stdio --url https://your-forgejo-instance.org
|
||
|
||
# Or pin a specific version
|
||
podman run --rm -i codeberg.org/goern/forgejo-mcp:v2.24.0 --help
|
||
```
|
||
|
||
| Tag | Meaning |
|
||
|-----------------|----------------------------------------------------------------|
|
||
| `vMAJOR.MINOR.PATCH` | Immutable — the exact release (e.g. `v2.24.0`). Use in production. |
|
||
| `latest` | Moving — tracks the most recent release. Convenience only. |
|
||
|
||
The image is single-arch (`linux/amd64`), signed with cosign, and carries an
|
||
attached CycloneDX SBOM. See [Verify the container image](#6-verify-the-container-image)
|
||
to check the signature and provenance before running.
|
||
|
||
### 2. Get Your Access Token
|
||
|
||
1. Log into your Forgejo instance
|
||
2. Go to **Settings** → **Applications** → **Access Tokens**
|
||
3. Create a new token with the permissions you need (repo, issue, etc.)
|
||
|
||
### 3. Configure Your AI Assistant
|
||
|
||
Add this to your MCP configuration file:
|
||
|
||
**For stdio mode** (most common):
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"forgejo": {
|
||
"command": "forgejo-mcp",
|
||
"args": [
|
||
"--transport", "stdio",
|
||
"--url", "https://your-forgejo-instance.org"
|
||
],
|
||
"env": {
|
||
"FORGEJO_ACCESS_TOKEN": "<your personal access token>",
|
||
"FORGEJO_USER_AGENT": "forgejo-mcp/1.0.0"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**For streamable HTTP mode** (recommended for remote/Claude.ai):
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"forgejo": {
|
||
"url": "http://localhost:8080/mcp"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
When using streamable HTTP mode, start the server first:
|
||
|
||
```bash
|
||
forgejo-mcp --transport http --url https://your-forgejo-instance.org --token <your-token>
|
||
```
|
||
|
||
**Multi-tenant HTTP mode** (optional):
|
||
|
||
You can run a single centralized `forgejo-mcp` instance and let each client provide its own token via the standard HTTP `Authorization` header. This enables serving multiple users or agents from one server.
|
||
|
||
1. Start the server (optionally without any global token):
|
||
```bash
|
||
forgejo-mcp --transport http --url https://your-forgejo-instance.org
|
||
```
|
||
2. Clients include their specific token in each request:
|
||
- `Authorization: token <token>` (Forgejo style)
|
||
- `Authorization: Bearer <token>` (OAuth2/MCP style)
|
||
- Note: The scheme (`token` or `Bearer`) is case-insensitive.
|
||
|
||
See [demos/multi-tenant-http.md](demos/multi-tenant-http.md) for a copy-pasteable walkthrough.
|
||
|
||
> Design rationale, token-resolution rules, and request-isolation guarantees: see the `stateless-http-auth` OpenSpec change (`openspec/changes/archive/`).
|
||
|
||
**For SSE mode** (legacy HTTP-based):
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"forgejo": {
|
||
"url": "http://localhost:8080/sse"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
When using SSE mode, start the server first:
|
||
|
||
```bash
|
||
forgejo-mcp --transport sse --url https://your-forgejo-instance.org --token <your-token>
|
||
```
|
||
|
||
### 4. Start Using It
|
||
|
||
Open your MCP-compatible AI assistant and try:
|
||
|
||
```
|
||
List all my repositories
|
||
```
|
||
|
||
## Available Tools
|
||
|
||
| Tool | Description |
|
||
|------|-------------|
|
||
| **User** | |
|
||
| `get_my_user_info` | Get information about the authenticated user |
|
||
| `check_notifications` | Check and list user notifications |
|
||
| `get_notification_thread` | Get detailed info on a single notification thread |
|
||
| `mark_notification_read` | Mark a single notification thread as read |
|
||
| `mark_all_notifications_read` | Acknowledge all notifications |
|
||
| `list_repo_notifications` | Filter notifications scoped to a single repository |
|
||
| `mark_repo_notifications_read` | Mark all notifications in a specific repo as read |
|
||
| `search_users` | Search for users |
|
||
| **Repositories** | |
|
||
| `list_my_repos` | List all repositories you own |
|
||
| `create_repo` | Create a new repository |
|
||
| `fork_repo` | Fork a repository |
|
||
| `search_repos` | Search for repositories |
|
||
| **Branches** | |
|
||
| `list_branches` | List all branches in a repository |
|
||
| `create_branch` | Create a new branch |
|
||
| `delete_branch` | Delete a branch |
|
||
| **Branch Protection** | |
|
||
| `list_branch_protections` | List a repository's branch protection rules. Bounded by `page` (1-based) + `limit` (page size); the response echoes `page`/`limit` so callers can fetch the next page. |
|
||
| `get_branch_protection` | Get a single rule by `rule` name |
|
||
| `create_branch_protection` | Create a rule. Requires `branch_name`; `status_check_contexts` is a comma-separated list of required checks (e.g. `"ci/build,ci/test"`). |
|
||
| `edit_branch_protection` | Edit a rule by `rule` name. Only fields you pass change; omitted fields are left unchanged. |
|
||
| `delete_branch_protection` | Delete a rule by `rule` name |
|
||
| **Files** | |
|
||
| `get_file_content` | Get the content of a file. Optional `start_line`/`end_line` request a 1-indexed inclusive line range (clamps to file extent; ignored when `with_metadata=true`). |
|
||
| `create_file` | Create a new file |
|
||
| `update_file` | Update an existing file |
|
||
| `delete_file` | Delete a file |
|
||
| **Commits** | |
|
||
| `list_repo_commits` | List commits in a repository |
|
||
| **Issues** | |
|
||
| `list_repo_issues` | List issues in a repository |
|
||
| `get_issue_by_index` | Get a specific issue |
|
||
| `create_issue` | Create a new issue |
|
||
| `add_issue_labels` | Add labels to an issue (requires numeric label IDs) |
|
||
| `remove_issue_labels` | Remove labels from an issue (requires numeric label IDs) |
|
||
| `update_issue` | Update an existing issue (requires numeric milestone ID) |
|
||
| `issue_state_change` | Open or close an issue |
|
||
| `list_repo_milestones` | List milestones with their IDs (use with `update_issue`) |
|
||
| `list_repo_labels` | List labels with their IDs. Merges org-level labels for org-owned repos (set `include_org_labels=false` to opt out). Each entry carries a `scope` field (`"repo"` or `"org"`). |
|
||
| `list_org_labels` | List organization-level labels with their IDs (use with `add_issue_labels`, `remove_issue_labels`). |
|
||
| `create_repo_label` | Create a repository label (`name`, `color` as 6-digit hex, optional `description`). Returns numeric `id` for immediate use in `add_issue_labels`. |
|
||
| `edit_repo_label` | Edit a repository label (PATCH — only supplied fields change: `name`, `color`, `description`). |
|
||
| `delete_repo_label` | Delete a repository label. Refuses by default when the label is in use (reports count); set `delete_mode=force` to override. |
|
||
| `get_repo_label` | Get a single repository label by numeric `id`. |
|
||
| `create_org_label` | Create an organization-level label. Same fields as `create_repo_label`. |
|
||
| `edit_org_label` | Edit an organization-level label (PATCH semantics). |
|
||
| `delete_org_label` | Delete an organization-level label. In-use guard counts across visible org repos (best-effort); `delete_mode=force` overrides. |
|
||
| `get_org_label` | Get a single organization-level label by numeric `id`. |
|
||
| **Comments** | |
|
||
| `list_issue_comments` | List comments on an issue or PR |
|
||
| `get_issue_comment` | Get a specific comment |
|
||
| `create_issue_comment` | Add a comment to an issue or PR |
|
||
| `edit_issue_comment` | Edit a comment |
|
||
| `delete_issue_comment` | Delete a comment |
|
||
| **Pull Requests** | |
|
||
| `list_repo_pull_requests` | List pull requests in a repository |
|
||
| `get_pull_request_by_index` | Get a specific pull request |
|
||
| `create_pull_request` | Create a new pull request |
|
||
| `update_pull_request` | Update an existing pull request |
|
||
| `list_pull_reviews` | List reviews for a pull request |
|
||
| `get_pull_review` | Get a specific pull request review |
|
||
| `list_pull_review_comments` | List comments on a pull request review |
|
||
| `list_pull_request_files` | List changed files in a pull request (paginated). Use the returned filenames as the `file_path` argument to `get_pull_request_diff`. |
|
||
| `get_pull_request_diff` | Get the unified diff of a pull request. Optional `file_path` returns only that file's hunks (matches on either pre- or post-rename path). |
|
||
| `merge_pull_request` | Merge a pull request (style: merge/rebase/rebase-merge/squash; optional title/message/delete-branch/force-merge/wait-for-checks). |
|
||
| `create_pull_review` | Create a review on a pull request (state: APPROVED/REQUEST_CHANGES/COMMENT) with optional inline comments. |
|
||
| **Actions** | |
|
||
| `dispatch_workflow` | Trigger a workflow run via `workflow_dispatch` event |
|
||
| `list_workflow_runs` | List workflow runs with optional filtering by status, event, or SHA |
|
||
| `get_workflow_run` | Get details of a specific workflow run by ID |
|
||
| **Organizations** | |
|
||
| `search_org_teams` | Search for teams in an organization |
|
||
| **Time Tracking** | |
|
||
| `list_issue_tracked_times` | List tracked time entries on an issue or PR |
|
||
| `list_repo_tracked_times` | List tracked time entries across a repository |
|
||
| `list_my_tracked_times` | List your own tracked time entries |
|
||
| `add_issue_time` | Log time against an issue or PR (accepts seconds or duration like `15m`) |
|
||
| `reset_issue_time` | Delete ALL tracked time entries on an issue or PR (destructive) |
|
||
| `delete_issue_time_entry` | Delete a single tracked time entry by ID |
|
||
| `start_issue_stopwatch` | Start a stopwatch on an issue or PR |
|
||
| `stop_issue_stopwatch` | Stop a running stopwatch and record the elapsed time |
|
||
| `cancel_issue_stopwatch` | Cancel a running stopwatch without recording |
|
||
| `list_my_stopwatches` | List currently running stopwatches |
|
||
| **Attachments** | |
|
||
| `list_issue_attachments` | List attachments on an issue or PR |
|
||
| `get_issue_attachment` | Get metadata for a single issue/PR attachment |
|
||
| `download_issue_attachment` | Download an issue/PR attachment (inline if < 1 MiB; metadata + URL otherwise) |
|
||
| `create_issue_attachment` | Upload a new attachment to an issue or PR (base64 content) |
|
||
| `edit_issue_attachment` | Rename an issue/PR attachment |
|
||
| `delete_issue_attachment` | Delete an issue/PR attachment |
|
||
| `list_comment_attachments` | List attachments on an issue/PR comment |
|
||
| `get_comment_attachment` | Get metadata for a single comment attachment |
|
||
| `download_comment_attachment` | Download a comment attachment (inline if < 1 MiB; metadata + URL otherwise) |
|
||
| `create_comment_attachment` | Upload a new attachment to an issue/PR comment (base64 content) |
|
||
| `edit_comment_attachment` | Rename a comment attachment |
|
||
| `delete_comment_attachment` | Delete a comment attachment |
|
||
| **Releases** | |
|
||
| `list_releases` | List releases for a repository (page/limit + client-side `state` filter: all/draft/prerelease/published) |
|
||
| `get_release_by_id` | Get a release by numeric ID |
|
||
| `get_release_by_tag` | Get a release by tag name |
|
||
| `get_latest_release` | Get the latest non-draft, non-prerelease release |
|
||
| `create_release` | Create a new release (pass `target_commitish` to also create the tag) |
|
||
| `edit_release` | Update fields of an existing release (only supplied fields are sent) |
|
||
| `delete_release` | Delete a release by numeric ID — destructive |
|
||
| `delete_release_by_tag` | Delete a release by tag name — destructive, verify tag |
|
||
| `list_release_attachments` | List attachments on a release (response fetched in full, sliced client-side) |
|
||
| `get_release_attachment` | Get metadata for a single release attachment |
|
||
| `download_release_attachment` | Download a release attachment (inline if < 1 MiB; metadata + URL otherwise) |
|
||
| `create_release_attachment` | Upload a new attachment to a release (base64 content) |
|
||
| `edit_release_attachment` | Rename a release attachment |
|
||
| `delete_release_attachment` | Delete a release attachment — destructive |
|
||
| **Server** | |
|
||
| `get_forgejo_mcp_server_version` | Get the MCP server version |
|
||
|
||
## Resources
|
||
|
||
MCP resource templates expose Forgejo entities as URI-addressable resources using the `forgejo://` scheme. The URI scheme is instance-portable — the same URI form works against any Forgejo instance — and does not collide with Forgejo web links. Clients that support `resources/templates/list` and `resources/read` (Claude Code, Claude Desktop, Codex, Cursor) can resolve these URIs directly. Clients without resource-template support continue to use the tools above — no functionality is removed.
|
||
|
||
Resources do NOT replace any MCP tool — every existing list/get tool stays available; resources are an additive, URI-addressable read surface intended for auto-resolution from LLM context and for content-addressable caching of immutable entities like commits.
|
||
|
||
Resources that embed a list (issue, pr) cap the embedded array at 30 items. When truncated, the JSON payload includes a sentinel naming the corresponding `list_*` tool the caller should invoke for the full list.
|
||
|
||
**When to use resources vs tools:** prefer a resource when you have a specific sha or index in hand; prefer a tool when listing or searching.
|
||
|
||
| URI Template | Entity | Notes |
|
||
|---|---|---|
|
||
| `forgejo://owner/{owner}` | application/json | User or org profile addressed by login; resolves user first, falls back to org. |
|
||
| `forgejo://repo/{owner}/{repo}` | application/json | Repository overview: identity + counts, no embedded lists. |
|
||
| `forgejo://repo/{owner}/{repo}/commit/{sha}` | Commit metadata | Immutable per sha. Returns JSON + markdown sidecar. sha must be 40 hex chars. |
|
||
| `forgejo://repo/{owner}/{repo}/commit/{sha}/status` | application/json | Combined CI status for a sha: aggregate state + bounded per-context statuses (cap 30, sentinel names list tool `get_commit_statuses`). |
|
||
| `forgejo://repo/{owner}/{repo}/issue/{index}` | application/json (+ text/markdown sidecar) | Issue metadata + rendered body + bounded recent comments (cap 30, sentinel names `list_issue_comments`). |
|
||
| `forgejo://repo/{owner}/{repo}/{kind}/{index}/comment/{id}` | application/json (+ text/markdown sidecar) | Single comment by id; kind ∈ {issue, pr}. |
|
||
| `forgejo://repo/{owner}/{repo}/pr/{index}` | application/json (+ text/markdown sidecar) | PR metadata, head/base refs, mergeability, bounded recent comments (cap 30, sentinel `list_issue_comments`) and reviews (cap 30, sentinel `list_pull_reviews`). |
|
||
| `forgejo://repo/{owner}/{repo}/label/{id}` | application/json | Single repository label by numeric id. |
|
||
| `forgejo://repo/{owner}/{repo}/labels{?page,limit}` | application/json | Bounded list of repository labels (cap 30, sentinel names `list_repo_labels`). |
|
||
| `forgejo://org/{org}/labels{?page,limit}` | application/json | Bounded list of organization-level labels (cap 30, sentinel names `list_org_labels`). |
|
||
|
||
### Client Compatibility
|
||
|
||
| Client | `resources/templates/list` | `resources/read` |
|
||
|---|---|---|
|
||
| Claude Code | supported | supported |
|
||
| Claude Desktop | supported | supported |
|
||
| Codex | supported | supported |
|
||
| Cursor (current) | supported | supported |
|
||
| Older / minimal clients | tools only | tools only |
|
||
|
||
## Demos
|
||
|
||
End-to-end, copy-pasteable walkthroughs of the tools above — grouped by
|
||
topic (labels, attachments, time tracking, notifications, orgs, bounded
|
||
I/O code review, transport) — live in [demos/](demos/README.md). Each
|
||
demo pairs real `./forgejo-mcp --cli` invocations with the output they
|
||
produced against `codeberg.org`.
|
||
|
||
## CLI Mode
|
||
|
||
You can invoke any tool directly from the command line without running an MCP server. This is useful for shell scripts, CI/CD pipelines, and Claude Code skills.
|
||
|
||
```bash
|
||
# List all available tools (grouped by domain)
|
||
forgejo-mcp --cli list
|
||
|
||
# Invoke a tool with JSON arguments
|
||
forgejo-mcp --cli get_issue_by_index --args '{"owner":"goern","repo":"forgejo-mcp","index":1}'
|
||
|
||
# Pipe JSON arguments via stdin
|
||
echo '{"owner":"goern","repo":"forgejo-mcp"}' | forgejo-mcp --cli list_repo_issues
|
||
|
||
# List recent workflow runs (text output)
|
||
forgejo-mcp --cli list_workflow_runs \
|
||
--args '{"owner":"goern","repo":"forgejo-mcp"}' \
|
||
--output=text
|
||
|
||
# List only failed runs
|
||
forgejo-mcp --cli list_workflow_runs \
|
||
--args '{"owner":"goern","repo":"forgejo-mcp","status":"failure"}' \
|
||
--output=text
|
||
|
||
# Show a tool's parameters
|
||
forgejo-mcp --cli create_issue --help
|
||
|
||
# Control output format (json or text)
|
||
forgejo-mcp --cli list --output=json
|
||
forgejo-mcp --cli get_my_user_info --args '{}' --output=text
|
||
```
|
||
|
||
CLI mode requires the same `FORGEJO_URL` and `FORGEJO_ACCESS_TOKEN` configuration as MCP server mode. Tool results are written as JSON to stdout by default; errors go to stderr with a non-zero exit code.
|
||
|
||
## Configuration Options
|
||
|
||
You can configure the server using command-line arguments or environment variables:
|
||
|
||
| CLI Argument | Environment Variable | Description |
|
||
|--------------|---------------------|-------------|
|
||
| `--url` | `FORGEJO_URL` | Your Forgejo instance URL |
|
||
| `--token` | `FORGEJO_ACCESS_TOKEN` | Your personal access token |
|
||
| `--debug` | `FORGEJO_DEBUG` | Enable debug mode |
|
||
| `--transport` | - | Transport mode: `stdio`, `sse`, or `http` |
|
||
| `--sse-port` | - | Port for SSE mode (default: 8080) |
|
||
| `--http-port` | - | Port for streamable HTTP mode (default: 8080) |
|
||
| `--cli` | - | Enter CLI mode for direct tool invocation |
|
||
| `--user-agent` | `FORGEJO_USER_AGENT` | HTTP User-Agent header (default: `forgejo-mcp/<version>`) |
|
||
|
||
Command-line arguments take priority over environment variables.
|
||
|
||
## Verifying Releases
|
||
|
||
Release archives are accompanied by a `checksums.txt` file and an optional
|
||
`checksums.txt.sig` produced by [cosign](https://github.com/sigstore/cosign)
|
||
with the project's release keypair. Verifying both files lets you confirm
|
||
that the binary you downloaded was built by the project's release pipeline
|
||
and has not been tampered with in transit.
|
||
|
||
> **Heads up:** cosign signing was introduced mid-2026. Tags released
|
||
> before signing was wired up ship without a `.sig` file — verification
|
||
> applies from `v2.23.x` onward only, and only when the
|
||
> `COSIGN_PRIVATE_KEY` secret was configured at release time.
|
||
|
||
### 1. Install cosign
|
||
|
||
Follow the upstream
|
||
[cosign installation guide](https://docs.sigstore.dev/cosign/system_config/installation/)
|
||
for your platform. Quick paths:
|
||
|
||
```bash
|
||
# Linux/macOS — pinned binary
|
||
COSIGN_VERSION=v2.4.1
|
||
curl -sSfL -o /usr/local/bin/cosign \
|
||
"https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64"
|
||
chmod +x /usr/local/bin/cosign
|
||
|
||
# macOS via Homebrew
|
||
brew install cosign
|
||
|
||
# Arch Linux
|
||
sudo pacman -S cosign
|
||
```
|
||
|
||
Confirm:
|
||
|
||
```bash
|
||
cosign version
|
||
```
|
||
|
||
### 2. Fetch the public key
|
||
|
||
The normative source for the cosign public key is the
|
||
[`op1st-emea-b4mad`](https://codeberg.org/operate-first/op1st-emea-b4mad)
|
||
GitOps repo — same source of truth that provisions the
|
||
`cosign-signing-key-artifacts` Secret in the `op1st-pipelines` namespace
|
||
where the release pipeline runs. This is the **artifact-signing** key that
|
||
signs release blobs (`checksums.txt.sig`); it is distinct from the
|
||
**image-signing** key `cosign-signing-key-images.pub` used in §5–§6 below.
|
||
Two ways to fetch it:
|
||
|
||
**Branch-tip (live, follows future key rotations):**
|
||
|
||
```bash
|
||
curl -sSfL -o cosign.pub \
|
||
https://codeberg.org/operate-first/op1st-emea-b4mad/raw/branch/main/manifests/applications/op1st-pipelines-tokens/cosign-signing-key-artifacts.pub
|
||
```
|
||
|
||
**Commit-pinned (tamper-evident, recommended for CI/scripts):**
|
||
|
||
```bash
|
||
curl -sSfL -o cosign.pub \
|
||
https://codeberg.org/operate-first/op1st-emea-b4mad/raw/commit/cd3715fa8283a2069a2e3e299744a7b55b1b0260/manifests/applications/op1st-pipelines-tokens/cosign-signing-key-artifacts.pub
|
||
```
|
||
|
||
The commit-pinned permalink hashes its content into the URL — if anyone
|
||
ever rewrites the file at that commit, your download fails or mismatches.
|
||
Pin to the latest commit that you trust before adopting the key in
|
||
automation.
|
||
|
||
### 3. Download the release artifacts
|
||
|
||
Pick the tag you installed (e.g. `v2.23.1`) and grab the checksum file,
|
||
its signature, and the binary archive:
|
||
|
||
```bash
|
||
TAG=v2.23.1
|
||
VERSION="${TAG#v}"
|
||
BASE="https://codeberg.org/goern/forgejo-mcp/releases/download/${TAG}"
|
||
|
||
curl -sSfLO "${BASE}/forgejo-mcp_${VERSION}_checksums.txt"
|
||
curl -sSfLO "${BASE}/forgejo-mcp_${VERSION}_checksums.txt.sig"
|
||
curl -sSfLO "${BASE}/forgejo-mcp_${VERSION}_linux_amd64.tar.gz" # adjust os/arch
|
||
```
|
||
|
||
### 4. Verify the signature, then the checksum
|
||
|
||
Cosign verifies that `checksums.txt` was signed by the holder of the
|
||
private key matching `cosign.pub`. Once the checksum file is trusted, a
|
||
plain `sha256sum -c` check confirms the archive's integrity.
|
||
|
||
```bash
|
||
# Verify checksums.txt against the signature.
|
||
cosign verify-blob \
|
||
--key cosign.pub \
|
||
--signature "forgejo-mcp_${VERSION}_checksums.txt.sig" \
|
||
"forgejo-mcp_${VERSION}_checksums.txt"
|
||
# Expected: "Verified OK"
|
||
|
||
# Verify the downloaded archive against the (now-trusted) checksums.
|
||
sha256sum --ignore-missing -c "forgejo-mcp_${VERSION}_checksums.txt"
|
||
# Expected: "<archive>: OK"
|
||
```
|
||
|
||
The checksum chain transitively covers the SBOMs and other per-archive
|
||
assets — verifying `checksums.txt` once is sufficient for everything
|
||
listed inside it.
|
||
|
||
### 5. Verify SLSA provenance for the release-tools image
|
||
|
||
The release-tools container image (used internally by the Tekton release
|
||
pipeline) carries SLSA v1.0 provenance generated by [Tekton
|
||
Chains](https://tekton.dev/docs/chains/). This attestation binds the image
|
||
digest to the exact PipelineRun, git commit, and builder identity that
|
||
produced it — providing supply-chain provenance beyond what the cosign
|
||
signature alone can attest.
|
||
|
||
Fetch the `cosign-signing-key-images` public key (a separate key from the
|
||
artifact-signing key above):
|
||
|
||
```bash
|
||
curl -sSfL -o cosign-images.pub \
|
||
https://codeberg.org/operate-first/op1st-emea-b4mad/raw/branch/main/manifests/applications/op1st-pipelines-tokens/cosign-signing-key-images.pub
|
||
```
|
||
|
||
Verify the attestation against a specific image tag:
|
||
|
||
```bash
|
||
IMAGE_TAG=v1.0.0 # substitute the release-tools tag you want to verify
|
||
cosign verify-attestation \
|
||
--type slsaprovenance \
|
||
--key cosign-images.pub \
|
||
"codeberg.org/operate-first/release-tools:${IMAGE_TAG}" \
|
||
| jq .
|
||
```
|
||
|
||
A successful run prints the decoded in-toto statement (JSON). Check that
|
||
`predicate.buildDefinition.externalParameters.runSpec.params` references the
|
||
expected git revision, and `predicate.runDetails.builder.id` shows the
|
||
Tekton Chains builder.
|
||
|
||
> **Note:** SLSA provenance attestations are available from releases built
|
||
> after forgejo-mcp-46j (Tekton Chains support) landed. Earlier image tags
|
||
> carry only the cosign signature; they have no `verify-attestation` payload.
|
||
|
||
### 6. Verify the container image
|
||
|
||
The `codeberg.org/goern/forgejo-mcp` application image ([Option D](#1-install))
|
||
is signed by the same `cosign-signing-key-images` key as the release-tools
|
||
image, carries an attached CycloneDX SBOM, and gets SLSA v1.0 provenance from
|
||
Tekton Chains. Reuse the `cosign-images.pub` key fetched above.
|
||
|
||
Verify the signature:
|
||
|
||
```bash
|
||
IMAGE_TAG=v2.24.0 # substitute the release you are pulling
|
||
cosign verify \
|
||
--key cosign-images.pub \
|
||
"codeberg.org/goern/forgejo-mcp:${IMAGE_TAG}" \
|
||
| jq .
|
||
```
|
||
|
||
Verify the SLSA provenance attestation:
|
||
|
||
```bash
|
||
cosign verify-attestation \
|
||
--type slsaprovenance \
|
||
--key cosign-images.pub \
|
||
"codeberg.org/goern/forgejo-mcp:${IMAGE_TAG}" \
|
||
| jq .
|
||
```
|
||
|
||
Verify and download the signed CycloneDX SBOM attestation:
|
||
|
||
```bash
|
||
cosign verify-attestation \
|
||
--type cyclonedx \
|
||
--key cosign-images.pub \
|
||
"codeberg.org/goern/forgejo-mcp:${IMAGE_TAG}" \
|
||
| jq -r '.payload | @base64d | fromjson | .predicate' > forgejo-mcp.cdx.json
|
||
```
|
||
|
||
> The SBOM is now a **signed** in-toto attestation (`cosign attest`), not an
|
||
> unsigned `attach sbom` artifact. `cosign download sbom` no longer applies.
|
||
|
||
Because the publish pipeline pushes by digest and only promotes the
|
||
`vX.Y.Z` / `latest` tags *after* signing and SBOM attachment succeed, any tag
|
||
you can pull is guaranteed to be signed.
|
||
|
||
### Troubleshooting verification
|
||
|
||
- **`Error: no matching signatures`** — the `.sig` file is from a
|
||
different release, or `cosign.pub` is the wrong key. Re-download both
|
||
from the same tag.
|
||
- **`Error: cannot read file: checksums.txt.sig`** — release predates
|
||
cosign signing, or signing was skipped that run because the secret was
|
||
unset. Fall back to the checksum-only check (`sha256sum -c`), which
|
||
still detects in-transit corruption but not tampering.
|
||
- **Mismatch between `cosign.pub` and the signature** — confirm you
|
||
fetched the public key from a commit that includes the key in use at
|
||
the time of the release. If in doubt, fetch from `branch/main`.
|
||
|
||
## Troubleshooting
|
||
|
||
**Enable debug mode** to see detailed logs:
|
||
|
||
```bash
|
||
forgejo-mcp --transport sse --url <url> --token <token> --debug
|
||
```
|
||
|
||
Or set the environment variable:
|
||
|
||
```bash
|
||
export FORGEJO_DEBUG=true
|
||
```
|
||
|
||
**Custom User-Agent**: If your Forgejo instance or proxy blocks the default `go-http-client` user agent, set a custom one:
|
||
|
||
```bash
|
||
# Via environment variable
|
||
export FORGEJO_USER_AGENT="forgejo-mcp/1.0.0"
|
||
|
||
# Or via CLI flag
|
||
forgejo-mcp --user-agent "forgejo-mcp/1.0.0" --transport sse --url <url> --token <token>
|
||
```
|
||
|
||
## Getting Help
|
||
|
||
- [Report issues](https://codeberg.org/goern/forgejo-mcp/issues)
|
||
- [View source code](https://codeberg.org/goern/forgejo-mcp)
|
||
|
||
This repository is also mirrored on [Radicle](https://radicle.xyz/) — a peer-to-peer code collaboration network. Clone via:
|
||
|
||
```bash
|
||
rad clone rad:z4PdPpsH9iJQcWfqTbxpFcWaZ9zPL
|
||
```
|
||
|
||
## For Developers
|
||
|
||
See [DEVELOPER.md](DEVELOPER.md) for build instructions, architecture overview, and contribution guidelines.
|
||
|
||
## Known Issues
|
||
|
||
- **`go install ...@latest` fails** — The `go.mod` contains a `replace` directive (for a forked Forgejo SDK), which prevents remote `go install`. Use the clone-and-build workflow shown in [Quick Start](#quick-start) instead. Tracked in [#67](https://codeberg.org/goern/forgejo-mcp/issues/67).
|
||
|
||
## Contributors
|
||
|
||
forgejo-mcp is shaped by everyone who files issues, writes code, reviews PRs, and pushes the project forward. Thank you all. 🙏
|
||
|
||
### Code contributors
|
||
|
||
| Contributor | Highlights |
|
||
|-------------|------------|
|
||
| [goern](https://codeberg.org/goern) (Christoph Görn) | Project creator and maintainer |
|
||
| Ronmi Ren | Co-creator; SSE/HTTP transport, issue blocking, CI/CD improvements, logo, Glama spec |
|
||
| [twstagg](https://codeberg.org/twstagg) (Tristin Stagg) | User agent configuration support (PR #89) |
|
||
| [mattdm](https://codeberg.org/mattdm) (Matthew Miller) | Logging improvements, FORGEJO_* migration, README, URL refactor |
|
||
| [byteflavour](https://codeberg.org/byteflavour) | `check_notifications` + full notification management API (PR #84, #86); stateless per-request auth for HTTP/SSE transports (PR #138); NixOS installation docs (PR #146); feature requests #80, #85 |
|
||
| [jesterret](https://codeberg.org/jesterret) | Pull request reviews and comments support (PR #51) |
|
||
| [appleboy](https://codeberg.org/appleboy) | Custom SSE port support, bug fixes |
|
||
| [ignasgil](https://codeberg.org/ignasgil) | `remove_issue_labels` tool (PR #96) |
|
||
| [dmikushin](https://codeberg.org/dmikushin) (Dmitry Mikushin) | Fix string-encoded number parameter parsing from MCP clients (PR #93) |
|
||
| [jiriks74](https://codeberg.org/jiriks74) | mcp-go v0.44.0 dependency update (PR #90) |
|
||
| [th](https://codeberg.org/th) (Tomi Haapaniemi) | `update_pull_request` tool |
|
||
| [hiifong](https://codeberg.org/hiifong) | Early bug fixes and updates |
|
||
| [Lunny Xiao](https://codeberg.org/lunny) | Early contributions |
|
||
| [techknowlogick](https://codeberg.org/techknowlogick) | Early contributions |
|
||
| [yp05327](https://codeberg.org/yp05327) | Early contributions |
|
||
| [mw75](https://codeberg.org/mw75) | Owner/org support for repo creation (PR #18) |
|
||
| [Dax Kelson](https://codeberg.org/dkelson) | Issue comment management (PR #34) |
|
||
| [Guruprasad Kulkarni](https://codeberg.org/comdotlinux) | Arch Linux AUR installation docs (PR #69) |
|
||
| [Mario Wolff](https://codeberg.org/mariowolff) | Contributions |
|
||
| [Massimo Fraschetti](https://codeberg.org/fraschetti) | Contributions |
|
||
| [synath](https://codeberg.org/synath) (David Paul Turley) | Repository-scoped token support via `ServerVersion` probe (PR #112); merge status-code check (PR #113); Claude Desktop Extension (.mcpb) packaging (PR #118) |
|
||
| [BrilliantKahn](https://codeberg.org/BrilliantKahn) | `get_file_content` plain-text default (PR #116); `list_repo_contents` and `get_repo_tree` tools (PR #117). **First-ever open source contribution** — welcome aboard! 🎉 |
|
||
|
||
### Community contributors
|
||
|
||
Issue reporters and discussion participants who shaped the direction of the project:
|
||
|
||
| Contributor | Contributions |
|
||
|-------------|--------------|
|
||
| [byteflavour](https://codeberg.org/byteflavour) | Filed #80 (milestone/label discovery), #85 (notification API proposal); active reviewer in discussions |
|
||
| [choucavalier](https://codeberg.org/choucavalier) | Filed #82 (fix skill), #70 (macOS arm64 releases), #62 (binary releases & mise support) |
|
||
| [MalcolmMielle](https://codeberg.org/MalcolmMielle) | Filed #59 (PR review tools — since implemented) |
|
||
| [redbeard](https://codeberg.org/redbeard) | Filed #60 (Actions support — since implemented) |
|
||
| [c6sepl6p](https://codeberg.org/c6sepl6p) | Filed #72 (base64 encoding), #54 (merge pull request — since implemented) |
|
||
| [malik](https://codeberg.org/malik) | Filed #73 (version flag), #47 (Nix build fix) |
|
||
| [a2800276](https://codeberg.org/a2800276) | Filed #74 (OpenAI compatibility) |
|
||
| [simenandre](https://codeberg.org/simenandre) | Filed #49 (go install support) |
|
||
| [BasdP](https://codeberg.org/BasdP) | Filed #42 (Projects support) |
|
||
| [BoBeR182](https://codeberg.org/BoBeR182) | Filed #32 (wiki support) |
|
||
| [ignasgil](https://codeberg.org/ignasgil) | Filed #95 (`remove_issue_labels` feature request) |
|
||
| [Vokuar](https://codeberg.org/Vokuar) | Filed #99 (streamable HTTP transport support) |
|
||
| [janbaer](https://codeberg.org/janbaer) | Filed #98 (reply to review comment) |
|
||
| [fraschm98](https://codeberg.org/fraschm98) | Early issue reports |
|
||
| [heathen711](https://codeberg.org/heathen711) | Filed #106 (issue/comment attachments — since implemented); shaped the 1 MiB inline cap + `browser_download_url` fall-through design |
|
||
|
||
### Cyborg contributors
|
||
|
||
This project also received contributions from AI coding agents — submitted as regular PRs, reviewed by humans:
|
||
|
||
| Agent | Role | Contributions |
|
||
|-------|------|---------------|
|
||
| [brenner-axiom](https://codeberg.org/brenner-axiom) (b4-dev, B4arena) | AI dev agent | Organization management tools (PR #94); showboat demos (PR #97); `list_repo_milestones`, `list_repo_labels` tools (PR #83); race condition fix (PR #78); contributors docs (PR #87, #88); filed #76; code reviews |
|
||
| opencode | AI dev agent | Pull request reviews and comments support (PR #51) |
|
||
| claude-code | AI dev agent | `get_file_content` plain-text default and `list_repo_contents`/`get_repo_tree` tools, paired with [BrilliantKahn](https://codeberg.org/BrilliantKahn) (PR #116, #117) |
|
||
| b4mad-release-bot | Release automation | Automated changelog and release tagging |
|
||
| the #B4mad Renovate bot | Dependency updates | Automated dependency upgrades |
|
||
|
||
Want to contribute? Open an issue or pull request — all are welcome.
|
||
|
||
|
||
## License
|
||
|
||
This project is open source. See the repository for license details.
|