03e28c159e
Add a read-only MCP tool that calls Gitea's authenticated-user endpoint (GET /api/v1/user) and returns safe identity metadata only: username, display name, user id, email, server, and remote. This lets future review/merge workflows prove which Gitea account the MCP server is authenticated as, so self-review/self-merge can be detected before acting — the blocker discovered during PR #8 dogfooding. - Never returns the token, Authorization header, password, or secrets. - Fails closed with a clear error if identity cannot be determined. - No mutation; no profile switching; no review/approve/merge behavior. Tests: identity mapping, secret-redaction, fail-closed, unknown-remote. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
224 lines
7.9 KiB
Markdown
224 lines
7.9 KiB
Markdown
# 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
|
|
|
|
Authentication is configured via environment variables or a local `.env` file in the repository root (uses `python-dotenv`).
|
|
|
|
Create a `.env` file in the project root:
|
|
|
|
```bash
|
|
# Option A: Gitea Personal Access Tokens (Recommended)
|
|
GITEA_TOKEN_DADESCHOOLS="your_token_here"
|
|
GITEA_TOKEN_PRGS="your_token_here"
|
|
|
|
# Option B: Gitea Username & Password (fallback)
|
|
GITEA_USER_DADESCHOOLS="username"
|
|
GITEA_PASS_DADESCHOOLS="password"
|
|
GITEA_USER_PRGS="username"
|
|
GITEA_PASS_PRGS="password"
|
|
|
|
# Optional: Fallback to macOS Keychain (via git credential fill)
|
|
# GITEA_USE_KEYCHAIN=1
|
|
```
|
|
|
|
## 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_edit_pr` | Edit details of an existing pull request |
|
|
| `gitea_list_prs` | List pull requests with state/remote |
|
|
| `gitea_view_pr` | Get full details of a single pull request |
|
|
| `gitea_merge_pr` | Merge a pull request (merge, squash, or rebase) |
|
|
| `gitea_review_pr` | Submit a review on a pull request and optionally merge it |
|
|
| `gitea_delete_branch` | Delete a remote branch |
|
|
| `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_whoami` | Read-only: identify the authenticated Gitea account (safe metadata only) |
|
|
| `gitea_mark_issue` | Claim/release an issue (start/done) |
|
|
| `gitea_list_labels` | List all available labels in a repository |
|
|
| `gitea_create_label` | Create a new label with custom color |
|
|
| `gitea_set_issue_labels` | Replace all labels on an issue |
|
|
| `gitea_get_file` | Retrieve file content and SHA metadata |
|
|
| `gitea_commit_files` | Commit changes to multiple files atomically |
|
|
| `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.
|
|
|
|
<details>
|
|
<summary><strong>Antigravity (Google)</strong></summary>
|
|
|
|
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"`).
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>Claude Code (Anthropic)</strong></summary>
|
|
|
|
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.
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>Any MCP-compatible client</strong></summary>
|
|
|
|
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.
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>Codex / non-MCP tools</strong></summary>
|
|
|
|
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"
|
|
```
|
|
</details>
|
|
|
|
## 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`) |
|
|
| `edit_pr.py` | Edit a Pull Request (`--title`, `--body`, `--body-file`, etc.) |
|
|
| `review_pr.py` | Review and sign-off on a pull request (with optional merge) |
|
|
| `close_issue.py` | Close a specific issue |
|
|
| `mark_issue.py` | 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"
|
|
|
|
# Edit a PR's description or title
|
|
./edit_pr.py 155 --body "Updated description wording"
|
|
|
|
# Review and approve a PR, then automatically merge it
|
|
./review_pr.py --pr-number 12 --event APPROVE --body "Approved" --merge
|
|
|
|
# Close issue #5
|
|
./close_issue.py 5
|
|
|
|
# Claim an issue before working on it
|
|
./mark_issue.py 10 start
|
|
|
|
# Release when done
|
|
./mark_issue.py 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
|
|
|
|
```
|
|
gitea_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
|
|
edit_pr.py ← CLI: edit PRs
|
|
review_pr.py ← CLI: review PRs
|
|
manage_labels.py ← CLI: label management
|
|
close_issue.py ← CLI: close issues
|
|
mark_issue.py ← 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_python_cli.py` | `close_issue.py` + `mark_issue.py` CLI validation |
|
|
| `test_mirror_refs.py` | Flags, safety defaults, local integration tests |
|
|
|
|
All tests mock network and keychain access — no real API calls are made.
|