Complete Goose + Gitea integration guide

- Document exact installation steps for gitea-mcp v1.3.0
- Provide precise config.yaml entry with field reference
- Document SSH setup for git operations on port 222
- Add usage examples for all 53 MCP tools
- Comprehensive troubleshooting section
- Fix malformed filenames from earlier attempts
Ole Valente
2026-07-07 18:08:07 +02:00
parent b9e6473a83
commit 5fe44cf06a
8 changed files with 559 additions and 0 deletions
+78
@@ -0,0 +1,78 @@
# Configuration
This is the critical step. You need to add the Gitea MCP extension to Goose's configuration file.
## The Config File
Goose's configuration lives at `~/.config/goose/config.yaml`. You'll add an entry under the `extensions:` section.
## The Exact Entry
```yaml
extensions:
# ... other extensions ...
gitea:
enabled: true
type: stdio
name: Gitea
description: Manage Gitea repositories, issues, PRs, branches, releases, actions and more
cmd: /usr/local/bin/gitea-mcp
args:
- -t
- stdio
- -H
- https://YOUR_GITEA_INSTANCE
- -T
- YOUR_ACCESS_TOKEN
envs: {}
env_keys: []
timeout: 300
bundled: null
available_tools: []
```
Replace:
- `https://YOUR_GITEA_INSTANCE` with your Gitea server URL
- `YOUR_ACCESS_TOKEN` with the token from [Installation](Installation)
### Field Reference
| Field | Value | Notes |
|-------|-------|-------|
| `type` | `stdio` | Goose spawns the binary as a child process |
| `cmd` | `/usr/local/bin/gitea-mcp` | Full path to the binary (see below) |
| `args` | `-t stdio -H <url> -T <token>` | MCP transport mode, host, and token |
| `envs` | `{}` | Empty — token is in args, not env vars |
| `timeout` | `300` | 5 minutes for long-running operations |
| `bundled` | `null` | Marks this as user-installed, not bundled |
| `available_tools` | `[]` | Empty = allow all tools |
### Why the Full Path for `cmd`?
Goose uses a `SearchPaths` resolver that calls the Rust `which` crate to find the command. The `which` crate only reliably resolves short command names (like `npx`, `uvx`) from PATH — it does not handle absolute paths well. However, the fallback in Goose's `resolve_command` function falls back to `PathBuf::from(cmd)`, which works correctly with absolute paths.
If you prefer short names, you can use `cmd: gitea-mcp` — just make sure the binary is in a directory that `which` can find (PATH directories like `/usr/local/bin`).
## Restart and Start a New Session
1. **Fully quit Goose** (Cmd+Q on macOS)
2. **Relaunch Goose**
3. **Start a brand new chat session** — existing sessions cache their extension list and won't pick up the new extension
## Verify It Works
In the new chat, ask Goose:
> "List my Gitea repositories"
If Goose can list your repos via the MCP, the integration is working.
## Diagnostics
If something goes wrong, check:
- `~/.local/state/goose/logs/server/` — goosed logs show extension loading errors
- Run `gitea-mcp -v` to confirm the binary works
- Test the token: `curl -s -H "Authorization: token YOUR_TOKEN" https://YOUR_GITEA/api/v1/user`
See [Troubleshooting](Troubleshooting) for more.
+61
@@ -0,0 +1,61 @@
# Goose + Gitea — Complete Integration Guide
This guide documents how to integrate a **self-hosted Gitea instance** with **Goose** (the AI agent harness) using the official [gitea-mcp](https://gitea.com/gitea/gitea-mcp) MCP server — v1.3.0 with **53 tools**.
## Architecture
```
┌──────────┐ stdio MCP ┌──────────────┐ REST API ┌──────────┐
│ Goose │ ◄────────────────► │ gitea-mcp │ ◄────────────────► │ Gitea │
│ (agent) │ (stdin/stdout) │ (Go binary) │ (HTTPS, token) │ Server │
└──────────┘ └──────────────┘ └──────────┘
```
- **Goose spawns `gitea-mcp`** as a child process on demand — no background servers
- **gitea-mcp** translates MCP tool calls into Gitea REST API calls
- **SSH** (port 222) is configured separately for `git clone/push/pull`
## What You Get — 53 Tools
### Repositories & Files
`create_repo`, `fork_repo`, `list_my_repos`, `list_org_repos`, `search_repos`, `create_or_update_file`, `delete_file`, `get_file_contents`, `get_dir_contents`, `get_repository_tree`
### Branches & Tags
`create_branch`, `delete_branch`, `list_branches`, `create_tag`, `delete_tag`, `list_tags`, `get_tag`
### Commits & Releases
`list_commits`, `get_commit`, `create_release`, `delete_release`, `get_release`, `get_latest_release`, `list_releases`
### Issues & Pull Requests
`issue_read`, `issue_write`, `list_issues`, `search_issues`, `pull_request_read`, `pull_request_write`, `pull_request_review_write`, `list_pull_requests`
### Labels & Milestones
`label_read`, `label_write`, `milestone_read`, `milestone_write`
### Actions (CI/CD)
`actions_config_read`, `actions_config_write`, `actions_run_read`, `actions_run_write`
### Wiki & Packages
`wiki_read`, `wiki_write`, `package_read`, `package_write`
### Other
`get_me`, `get_user_orgs`, `search_org_teams`, `search_users`, `notification_read`, `notification_write`, `timetracking_read`, `timetracking_write`, `get_gitea_mcp_server_version`
## Setup Steps
1. **[Installation](Installation)** — Download and install the `gitea-mcp` binary
2. **[Configuration](Configuration)** — The exact `config.yaml` entry
3. **[SSH Setup](SSH-Setup)** — For git push/pull/clone over port 222
4. **Start a brand new chat** in Goose (existing sessions cache extensions)
5. **[Usage](Usage)** — Examples of what to ask Goose
6. **[Troubleshooting](Troubleshooting)** — Common issues and fixes
## Reference Setup
| Component | Value |
|-----------|-------|
| Gitea instance | `https://gt.covalente.dk` |
| SSH port | 222/tcp |
| gitea-mcp version | 1.3.0 |
| Binary path | `/usr/local/bin/gitea-mcp` |
| Goose version | 1.41.0 |
+81
@@ -0,0 +1,81 @@
# Installation
## 1. Download the gitea-mcp Binary
The official [gitea-mcp](https://gitea.com/gitea/gitea-mcp) project (from the Gitea team itself) provides prebuilt binaries.
### macOS (ARM64 / Apple Silicon)
```bash
curl -L -o /tmp/gitea-mcp.tar.gz \
"https://gitea.com/gitea/gitea-mcp/releases/download/v1.3.0/gitea-mcp_Darwin_arm64.tar.gz"
tar -xzf /tmp/gitea-mcp.tar.gz -C /tmp/
```
### macOS (Intel x86_64)
```bash
curl -L -o /tmp/gitea-mcp.tar.gz \
"https://gitea.com/gitea/gitea-mcp/releases/download/v1.3.0/gitea-mcp_Darwin_x86_64.tar.gz"
tar -xzf /tmp/gitea-mcp.tar.gz -C /tmp/
```
### Linux (x86_64)
```bash
curl -L -o /tmp/gitea-mcp.tar.gz \
"https://gitea.com/gitea/gitea-mcp/releases/download/v1.3.0/gitea-mcp_Linux_x86_64.tar.gz"
tar -xzf /tmp/gitea-mcp.tar.gz -C /tmp/
```
### Linux (ARM64)
```bash
curl -L -o /tmp/gitea-mcp.tar.gz \
"https://gitea.com/gitea/gitea-mcp/releases/download/v1.3.0/gitea-mcp_Linux_arm64.tar.gz"
tar -xzf /tmp/gitea-mcp.tar.gz -C /tmp/
```
### Windows
```bash
curl -L -o gitea-mcp.zip \
"https://gitea.com/gitea/gitea-mcp/releases/download/v1.3.0/gitea-mcp_Windows_x86_64.zip"
# Extract and place in PATH
```
## 2. Install to System PATH
```bash
sudo mv /tmp/gitea-mcp /usr/local/bin/gitea-mcp
sudo chmod +x /usr/local/bin/gitea-mcp
```
**Why `/usr/local/bin`?** Goose's `SearchPaths` resolver includes `/usr/local/bin` (and `/opt/homebrew/bin` on macOS). Placing the binary here ensures Goose can find it when spawning the MCP process.
## 3. Verify
```bash
gitea-mcp -v
# Output: 1.3.0
```
## 4. Get a Gitea Access Token
1. Go to your Gitea instance: **Settings → Applications → Manage Access Tokens**
2. Generate a new token with these scopes (at minimum):
- `read:repository`
- `write:repository`
- `read:issue`
- `write:issue`
- `read:user`
For full functionality, also enable:
- `write:package`
- `write:notification`
3. Save the token securely — you'll need it in the configuration step.
## 5. (Optional) Set Up SSH for Git Operations
The MCP handles Gitea API calls. For `git clone`, `git push`, and `git pull`, configure SSH separately — see [SSH Setup](SSH-Setup).
+58
@@ -0,0 +1,58 @@
# SSH Setup for Git Operations
The Gitea MCP handles all Gitea API operations (repos, issues, PRs, etc.) via HTTPS. However, **git operations** (`clone`, `push`, `pull`, `fetch`) go through SSH.
## Configure SSH for Gitea
Add to `~/.ssh/config`:
```
Host gt.covalente.dk
HostName gt.covalente.dk
Port 222
User git
IdentityFile ~/.ssh/your_ssh_key
```
Replace:
- `gt.covalente.dk` with your Gitea instance hostname
- `222` with your Gitea SSH port (default Gitea SSH is 222, not 22)
- `~/.ssh/your_ssh_key` with the path to your SSH private key
## Verify SSH Access
```bash
ssh -T git@gt.covalente.dk -p 222
```
Expected output (Gitea doesn't provide shell access, but confirms authentication):
```
Hi there, username! You've successfully authenticated with the key named your_key,
but Gitea does not provide shell access.
```
## Using SSH with Goose
Goose can run git commands via its shell tool. Once SSH is configured:
```
# Clone
git clone git@gt.covalente.dk:owner/repo.git
# Add, commit, push
git add .
git commit -m "message"
git push origin main
```
## SSH + MCP = Full Workflow
| Operation | Tool | Transport |
|-----------|------|-----------|
| Create repo | Gitea MCP (`create_repo`) | HTTPS API |
| Create issue | Gitea MCP (`issue_write`) | HTTPS API |
| Clone repo | Shell (`git clone`) | SSH port 222 |
| Push code | Shell (`git push`) | SSH port 222 |
| Create PR | Gitea MCP (`pull_request_write`) | HTTPS API |
| Merge PR | Gitea MCP (`pull_request_write`) | HTTPS API |
+136
@@ -0,0 +1,136 @@
# Troubleshooting
## Extension Not Showing in Goose
### Symptom
The Gitea tools don't appear in your Goose session even after configuring `config.yaml`.
### Fix: Start a New Session
Goose caches extensions per session. If you added the gitea extension to your config while a session was active, **the extension won't appear in that session**.
1. Start a **brand new chat** (not a continuation)
2. The fresh session picks up all enabled extensions from `config.yaml`
### Still Not Working?
1. Fully quit Goose (Cmd+Q) and relaunch
2. Start a new chat immediately — don't restore any conversations
3. Verify: ask "List my Gitea repositories"
## Binary Not Found
### Symptom
Goose logs show: `No such file or directory (os error 2)` when trying to spawn `gitea-mcp`.
### Fix
Make sure the binary is in a directory that Goose can find.
```bash
# Check binary exists and is executable
ls -la /usr/local/bin/gitea-mcp
/usr/local/bin/gitea-mcp -v
# If missing, reinstall
sudo cp /tmp/gitea-mcp /usr/local/bin/gitea-mcp
sudo chmod +x /usr/local/bin/gitea-mcp
```
### Goose's SearchPaths
Goose searches these directories (in order):
- `~/.local/bin`
- `/usr/local/bin`
- `/opt/homebrew/bin` (macOS)
- Your system `$PATH`
Put `gitea-mcp` in any of these directories and it will be found.
## Authentication Errors
### Symptom
Tools return `401 Unauthorized` or similar.
### Fix
1. Verify your token: `curl -s -H "Authorization: token YOUR_TOKEN" https://YOUR_GITEA/api/v1/user`
2. Check that the token in `config.yaml` args is correct
3. Ensure your token has the required scopes (read:repository, write:repository, etc.)
4. Tokens don't expire by default, but check your Gitea instance settings
## "Extension gitea is not valid"
### Symptom
Calling `listResources` for "gitea" returns "Extension gitea is not valid".
### Fix
This means the extension exists in the config but isn't loaded in the current session. **Start a new chat**. If that doesn't help, check the goosed logs:
```bash
ls -t ~/.local/state/goose/logs/server/2026-07-*/*.log | head -1 | xargs grep -i gitea
```
Look for errors like:
- `Failed to create extension 'gitea'` — the binary isn't found or can't be spawned
- `Skipping malformed extension config entry` — the YAML is invalid
- `Unknown ecosystem for command` — harmless info message, not an error
## Config Not Loading
### Symptom
The gitea extension doesn't appear in `~/.config/goose/config.yaml` after adding it.
### Fix
Make sure your YAML indentation is correct. The `gitea` entry must be at the same indentation level as other extensions (2 spaces from the left margin, under `extensions:`).
```yaml
extensions:
gitea: # ← 2 spaces
enabled: true # ← 4 spaces
type: stdio # ← 4 spaces
```
The `config.yaml` file is parsed by Goose's Rust backend. Invalid YAML causes the entry to be silently skipped.
## Viewing Goose Logs
Goose writes logs to:
| Log | Location | Contains |
|-----|----------|----------|
| goosed (server) | `~/.local/state/goose/logs/server/` | Extension loading, tool execution, errors |
| App (Electron) | `~/Library/Application Support/Goose/logs/main.log` (macOS) | Frontend events, UI errors |
| Startup | `~/Library/Application Support/Goose/logs/startup/` | goosed spawn events |
To tail the goosed log in real time:
```bash
tail -f ~/.local/state/goose/logs/server/$(date +%Y-%m-%d)/*.log | grep -i gitea
```
## Can't Clone/Push via SSH
### Symptom
`git clone` fails with "Permission denied" or "Connection refused".
### Fix
The MCP handles API calls via HTTPS. Git operations use **SSH on port 222** (Gitea's default SSH port, not 22).
Check your `~/.ssh/config`:
```
Host YOUR_GITEA_HOST
Port 222
User git
IdentityFile ~/.ssh/your_key
```
Verify: `ssh -T git@YOUR_GITEA_HOST -p 222`
See [SSH Setup](SSH-Setup) for details.
## Rate Limiting
Gitea may rate-limit API requests. The `gitea-mcp` binary uses pagination (default 30 items per page) and respects Gitea's rate limits. If you hit limits, increase `timeout` in the config or reduce the frequency of API calls.
## Need More Help?
- [gitea-mcp source repository](https://gitea.com/gitea/gitea-mcp) — report issues here
- Gitea instance: `https://gt.covalente.dk` — check server status
- Goose logs: `~/.local/state/goose/logs/server/` — detailed error information
+139
@@ -0,0 +1,139 @@
# Usage Examples
Once the Gitea MCP is configured and you've started a **new Goose session**, you can ask Goose to perform any Gitea operation. Here are examples.
## Repository Operations
```
Create a new public repo called "my-project" with a README
```
```
Fork the repo oval/goose-gitea-guide into my account
```
```
List all my repositories
```
```
Search for repositories matching "goose"
```
## File Operations
```
Create a file called "README.md" in oval/my-project with content
"# My Project\n\nA new project." on the main branch
```
```
Show me the contents of README.md in oval/goose-gitea-guide
```
```
Delete the file "old-config.yml" from oval/my-project
```
## Issues
```
Create an issue in oval/my-project titled "Add authentication"
with a description about needing OAuth support
```
```
List all open issues in oval/my-project
```
```
Close issue #3 in oval/my-project with a comment "Fixed in PR #5"
```
```
Search issues across all my repos for "bug" with label "critical"
```
## Pull Requests
```
Create a PR from branch feature-auth to main in oval/my-project
titled "Add OAuth authentication"
```
```
List all open pull requests in oval/my-project
```
```
Review PR #5 in oval/my-project and approve it
```
```
Merge PR #5 in oval/my-project using rebase
```
## Branches and Tags
```
Create a new branch called "feature-login" from main in oval/my-project
```
```
List all branches in oval/my-project
```
```
Create a tag v1.0.0 in oval/my-project
```
## Releases
```
Create a release v1.0.0 in oval/my-project with release notes
about the initial version
```
```
Show me the latest release for oval/my-project
```
## Wiki
```
Show me the Home page of the wiki for oval/my-project
```
```
Create a new wiki page called "API-Docs" in oval/my-project
with content about the API endpoints
```
## Actions/CI
```
List all workflows for oval/my-project
```
```
Show me recent workflow runs for oval/my-project
```
## Labels and Milestones
```
Create a "bug" label (red) and "enhancement" label (blue) in oval/my-project
```
```
Create a milestone "v1.0" in oval/my-project due next month
```
## Combined Workflow
Goose can combine multiple operations:
> Create a new repo "goose-bot", add a README, create a "good first issue"
> label, and create an issue titled "Welcome" with that label
All of this happens through the Gitea MCP — no manual API calls needed.
+6
@@ -0,0 +1,6 @@
- [Home](Home)
- [Installation](Installation)
- [Configuration](Configuration)
- [SSH Setup](SSH-Setup)
- [Usage](Usage)
- [Troubleshooting](Troubleshooting)