# Gitea Tools
A collection of Python scripts and an MCP server to automate interactions with Gitea instances.
## Supported Instances
| Remote | Host | Org / Repo |
|----------------|----------------------------|-------------------------------------|
| `dadeschools` | `gitea.dadeschools.net` | `Contractor / Timesheet` |
| `prgs` | `gitea.prgs.cc` | `Scaled-Tech-Consulting / Timesheet`|
## Authentication
Scripts extract credentials from the macOS keychain automatically — no tokens on the command line.
- **dadeschools** — HTTPS via `git credential fill` (SSH:2222 is flaky)
- **prgs** — SSH via `ssh://git@gitea-ssh.prgs.cc:2222` (SSH is reliable here)
Ensure you've logged in via Git over HTTPS at least once so the keychain caches your credentials.
## MCP Server (Recommended)
The Gitea-Tools MCP server exposes all functionality as structured tool calls.
Any MCP-compatible agent (Antigravity, Claude Code, etc.) can call these tools natively.
### Available Tools
| Tool | Description |
|------|-------------|
| `gitea_create_issue` | Create an issue with title, body, remote |
| `gitea_create_pr` | Open a pull request with title, head, base |
| `gitea_close_issue` | Close an issue by number |
| `gitea_list_issues` | List issues with state/label filters |
| `gitea_view_issue` | Get full details of a single issue |
| `gitea_mark_issue` | Claim/release an issue (start/done) |
| `gitea_mirror_refs` | Mirror branches + tags between instances |
### Setup
#### 1. Install dependencies
```bash
cd /Users/jasonwalker/Development/Gitea-Tools
python3 -m venv venv # skip if venv already exists
source venv/bin/activate
pip install "mcp[cli]"
```
#### 2. Configure your AI client
The MCP server uses **stdio transport** — each client starts it as a subprocess.
Add the config below to your client, then restart it.
Antigravity (Google)
Add to `~/.gemini/antigravity-ide/mcp_config.json` inside `"mcpServers"`:
```json
"gitea-tools": {
"command": "/Users/jasonwalker/Development/Gitea-Tools/venv/bin/python3",
"args": ["/Users/jasonwalker/Development/Gitea-Tools/mcp_server.py"],
"env": {}
}
```
Restart Antigravity to load the server. Tools appear as lazy-loaded MCP tools
(call via `call_mcp_tool` with `ServerName: "gitea-tools"`).
Claude Code (Anthropic)
Add to `~/.claude.json` (global) or `.mcp.json` in the project root:
```json
{
"mcpServers": {
"gitea-tools": {
"command": "/Users/jasonwalker/Development/Gitea-Tools/venv/bin/python3",
"args": ["/Users/jasonwalker/Development/Gitea-Tools/mcp_server.py"]
}
}
}
```
Restart Claude Code. Tools appear as `mcp__gitea-tools__gitea_create_issue`, etc.
Any MCP-compatible client
The server is a standard MCP stdio server. Point your client at:
- **Command:** `/Users/jasonwalker/Development/Gitea-Tools/venv/bin/python3`
- **Args:** `["/Users/jasonwalker/Development/Gitea-Tools/mcp_server.py"]`
- **Transport:** `stdio`
No environment variables needed — auth is handled via macOS keychain.
Codex / non-MCP tools
OpenAI Codex and other tools that don't support MCP can use the CLI scripts
directly. See the [CLI Scripts](#cli-scripts) section below.
```bash
# Example: Codex can shell out to the scripts
python3 /Users/jasonwalker/Development/Gitea-Tools/create_issue.py \
--remote prgs --title "Bug report" --body "Details here"
```
## CLI Scripts
The MCP tools can also be used as standalone CLI scripts:
| Script | Description |
|---------------------|--------------------------------------------------------------------|
| `create_issue.py` | Create an issue (`--remote`, `--title`, `--body`, `--body-file`) |
| `create_pr.py` | Open a Pull Request (`--remote`, `--title`, `--head`, `--base`) |
| `close_issue.sh` | Close a specific issue (dadeschools only) |
| `mark_issue.sh` | Claim/release an issue via `status:in-progress` label |
| `manage_labels.py` | Create label set and apply label mappings (`--dry` to preview) |
| `mirror_refs.sh` | Mirror branches + tags between dadeschools ⇄ prgs |
### Quick Examples
```bash
# Create an issue
./create_issue.py --title "Fix PDF output" --body "Blank on Safari"
# Create an issue on the prgs instance
./create_issue.py --remote prgs --title "Add tests" --body-file description.md
# Create a PR
./create_pr.py --title "feat: add validation" --head feat/validation --body "Closes #12"
# Close issue #5
./close_issue.sh 5
# Claim an issue before working on it
./mark_issue.sh 10 start
# Release when done
./mark_issue.sh 10 done
# Mirror refs (dry-run by default)
./mirror_refs.sh
# Actually push the refs
./mirror_refs.sh --apply
```
Use `--help` on any Python script or shell script for full usage details.
## Architecture
```
auth.py ← shared auth & API helpers (get_credentials, api_request)
mcp_server.py ← MCP server (FastMCP, stdio transport)
create_issue.py ← CLI: create issues
create_pr.py ← CLI: create PRs
manage_labels.py ← CLI: label management
close_issue.sh ← CLI: close issues
mark_issue.sh ← CLI: claim/release issues
mirror_refs.sh ← CLI: ref mirroring
```
## Tests
```bash
# Run with the venv (includes MCP SDK)
source venv/bin/activate
python3 -m pytest tests/ -v
```
| Test file | Covers |
|--------------------------|---------------------------------------------------------|
| `test_mcp_server.py` | All 7 MCP tools: create, list, view, close, mark, PR, mirror |
| `test_create_issue.py` | CLI arg parsing, remote resolution, payload, auth, errors |
| `test_create_pr.py` | CLI arg parsing, remote resolution, payload, auth, errors |
| `test_credentials.py` | `get_credentials()`, `get_auth_header()`, `repo_api_url()` |
| `test_manage_labels.py` | Label create/skip, dry run, mapping, constant validation |
| `test_shell_scripts.py` | `close_issue.sh` + `mark_issue.sh` arg validation |
| `test_mirror_refs.py` | Flags, safety defaults, local integration tests |
All tests mock network and keychain access — no real API calls are made.