Replaces the minimal project description with a comprehensive README including a component overview table, quick start instructions, common Ansible operations, and links to detailed documentation. Aligns with Red Panda Approval™ standards.
18 KiB
Gitea MCP Server - Red Panda Approved™
Model Context Protocol (MCP) server providing programmatic access to Gitea repositories, issues, and pull requests. Deployed as a Docker container on Miranda (MCP Docker Host) in the Agathos sandbox.
Overview
The Gitea MCP Server exposes Gitea's functionality through the MCP protocol, enabling AI assistants and automation tools to interact with Git repositories, issues, pull requests, and other Gitea features.
| Property | Value |
|---|---|
| Host | Miranda (10.10.0.156) |
| Service Port | 25535 |
| Container Port | 8000 |
| Transport | HTTP |
| Image | docker.gitea.com/gitea-mcp-server:latest |
| Gitea Instance | https://gitea.ouranos.helu.ca |
| Logging | Syslog to port 51435 → Alloy → Loki |
Purpose
- Repository Operations: Clone, read, and analyze repository contents
- Issue Management: Create, read, update, and search issues
- Pull Request Workflow: Manage PRs, reviews, and merges
- Code Search: Search across repositories and file contents
- User/Organization Info: Query user profiles and organization details
Integration Points
AI Assistant (Cline/Claude Desktop)
↓ (MCP Protocol)
MCP Switchboard (Oberon)
↓ (HTTP)
Gitea MCP Server (Miranda:25535)
↓ (Gitea API)
Gitea Instance (Rosalind:22083)
Architecture
Deployment Model
Container-Based: Single Docker container managed via Docker Compose
Directory Structure:
/srv/gitea_mcp/
└── docker-compose.yml # Container orchestration
System Integration:
- User/Group:
gitea_mcp:gitea_mcp(system user) - Ansible User Access: Remote user added to gitea_mcp group
- Permissions: Directory mode 750, compose file mode 550
Network Configuration
| Component | Port | Protocol | Purpose |
|---|---|---|---|
| External Access | 25535 | HTTP | MCP protocol endpoint |
| Container Internal | 8000 | HTTP | Service listening port |
| Syslog | 51435 | TCP | Log forwarding to Alloy |
Logging Pipeline
Gitea MCP Container
↓ (Docker syslog driver)
Local Syslog (127.0.0.1:51435)
↓ (Alloy collection)
Loki (Prospero)
↓ (Grafana queries)
Grafana Dashboards
Log Format: RFC5424 (syslog_format variable)
Log Tag: gitea-mcp
Prerequisites
Infrastructure Requirements
- Miranda Host: Docker engine installed and running
- Gitea Instance: Accessible Gitea server (gitea.ouranos.helu.ca)
- Access Token: Gitea personal access token with required permissions
- Monitoring Stack: Alloy configured for syslog collection (port 51435)
Required Permissions
Gitea Access Token Scopes:
repo: Full repository access (read/write)user: Read user informationorg: Read organization informationissue: Manage issuespull_request: Manage pull requests
Token Creation:
- Log into Gitea → User Settings → Applications
- Generate New Token → Select scopes
- Copy token (shown only once)
- Store in Ansible Vault as
vault_gitea_mcp_access_token
Ansible Dependencies
community.docker.docker_compose_v2collection- Docker Python SDK on Miranda
- Ansible Vault configured with password file
Configuration
Host Variables
All configuration is defined in ansible/inventory/host_vars/miranda.incus.yml:
services:
- gitea_mcp # Enable service on this host
# Gitea MCP Configuration
gitea_mcp_user: gitea_mcp
gitea_mcp_group: gitea_mcp
gitea_mcp_directory: /srv/gitea_mcp
gitea_mcp_port: 25535
gitea_mcp_host: https://gitea.ouranos.helu.ca
gitea_mcp_access_token: "{{ vault_gitea_mcp_access_token }}"
gitea_mcp_syslog_port: 51435
Variable Reference
| Variable | Purpose | Example |
|---|---|---|
gitea_mcp_user |
Service system user | gitea_mcp |
gitea_mcp_group |
Service system group | gitea_mcp |
gitea_mcp_directory |
Service root directory | /srv/gitea_mcp |
gitea_mcp_port |
External port binding | 25535 |
gitea_mcp_host |
Gitea instance URL | https://gitea.ouranos.helu.ca |
gitea_mcp_access_token |
Gitea API token (vault) | {{ vault_gitea_mcp_access_token }} |
gitea_mcp_syslog_port |
Local syslog port | 51435 |
Vault Configuration
Store the Gitea access token securely in ansible/inventory/group_vars/all/vault.yml:
---
# Gitea MCP Server Access Token
vault_gitea_mcp_access_token: "your_gitea_access_token_here"
Encrypt vault file:
ansible-vault encrypt ansible/inventory/group_vars/all/vault.yml
Edit vault file:
ansible-vault edit ansible/inventory/group_vars/all/vault.yml
Deployment
Initial Deployment
Prerequisites Check:
# Verify Miranda has Docker
ansible miranda.incus -m command -a "docker --version"
# Verify Miranda is in inventory
ansible miranda.incus -m ping
# Check Gitea accessibility
curl -I https://gitea.ouranos.helu.ca
Deploy Service:
cd ansible/
# Deploy only Gitea MCP service
ansible-playbook gitea_mcp/deploy.yml
# Or deploy as part of full stack
ansible-playbook site.yml
Deployment Process:
- ✓ Check service is enabled in host's
serviceslist - ✓ Create gitea_mcp system user and group
- ✓ Add Ansible remote user to gitea_mcp group
- ✓ Create /srv/gitea_mcp directory (mode 750)
- ✓ Template docker-compose.yml (mode 550)
- ✓ Reset SSH connection (apply group changes)
- ✓ Start Docker container via docker-compose
Deployment Output
Expected Success:
PLAY [Deploy Gitea MCP Server with Docker Compose] ****************************
TASK [Check if host has gitea_mcp service] ************************************
ok: [miranda.incus]
TASK [Create gitea_mcp group] *************************************************
changed: [miranda.incus]
TASK [Create gitea_mcp user] **************************************************
changed: [miranda.incus]
TASK [Add group gitea_mcp to Ansible remote_user] *****************************
changed: [miranda.incus]
TASK [Create gitea_mcp directory] *********************************************
changed: [miranda.incus]
TASK [Template docker-compose file] *******************************************
changed: [miranda.incus]
TASK [Reset SSH connection to apply group changes] ****************************
changed: [miranda.incus]
TASK [Start Gitea MCP service] ************************************************
changed: [miranda.incus]
PLAY RECAP ********************************************************************
miranda.incus : ok=8 changed=7 unreachable=0 failed=0
Verification
Container Status
Check container is running:
# Via Ansible
ansible miranda.incus -m command -a "docker ps | grep gitea-mcp"
# Direct SSH
ssh miranda.incus
docker ps | grep gitea-mcp
Expected Output:
CONTAINER ID IMAGE STATUS PORTS
abc123def456 docker.gitea.com/gitea-mcp-server:latest Up 2 minutes 0.0.0.0:25535->8000/tcp
Service Connectivity
Test MCP endpoint:
# From Miranda
curl -v http://localhost:25535
# From other hosts
curl -v http://miranda.incus:25535
Expected Response: HTTP response indicating MCP server is listening
Log Inspection
Docker logs:
ssh miranda.incus
docker logs gitea-mcp
Centralized logs via Loki:
# Via logcli (if installed)
logcli query '{job="syslog", container_name="gitea-mcp"}' --limit=50
# Via Grafana Explore
# Navigate to: https://grafana.ouranos.helu.ca
# Select Loki datasource
# Query: {job="syslog", container_name="gitea-mcp"}
Functional Testing
Test Gitea API access:
# Enter container
ssh miranda.incus
docker exec -it gitea-mcp sh
# Test Gitea API connectivity (if curl available in container)
# Note: Container may not have shell utilities
MCP Protocol Test (from client):
# Using MCP inspector or client tool
mcp connect http://miranda.incus:25535
# Or test via MCP Switchboard
curl -X POST http://oberon.incus:22781/mcp/invoke \
-H "Content-Type: application/json" \
-d '{"server":"gitea","method":"list_repositories"}'
Management
Updating the Service
Update container image:
cd ansible/
# Re-run deployment (pulls latest image)
ansible-playbook gitea_mcp/deploy.yml
Docker Compose will:
- Pull latest
docker.gitea.com/gitea-mcp-server:latestimage - Recreate container if image changed
- Preserve configuration from docker-compose.yml
Restarting the Service
Via Docker Compose:
ssh miranda.incus
cd /srv/gitea_mcp
docker compose restart
Via Docker:
ssh miranda.incus
docker restart gitea-mcp
Via Ansible (re-run deployment):
ansible-playbook gitea_mcp/deploy.yml
Removing the Service
Complete removal:
cd ansible/
ansible-playbook gitea_mcp/remove.yml
Remove playbook actions:
- Stop and remove Docker containers
- Remove Docker volumes
- Remove Docker images
- Prune unused Docker images
- Remove /srv/gitea_mcp directory
Manual cleanup (if needed):
ssh miranda.incus
# Stop and remove container
cd /srv/gitea_mcp
docker compose down -v --rmi all
# Remove directory
sudo rm -rf /srv/gitea_mcp
# Remove user/group (optional)
sudo userdel gitea_mcp
sudo groupdel gitea_mcp
Configuration Changes
Update Gitea host or port:
- Edit
ansible/inventory/host_vars/miranda.incus.yml - Modify
gitea_mcp_hostorgitea_mcp_port - Re-run deployment:
ansible-playbook gitea_mcp/deploy.yml
Rotate access token:
- Generate new token in Gitea
- Update vault:
ansible-vault edit ansible/inventory/group_vars/all/vault.yml - Update
vault_gitea_mcp_access_tokenvalue - Re-run deployment to update environment variable
Troubleshooting
Container Won't Start
Symptom: Container exits immediately or won't start
Diagnosis:
ssh miranda.incus
# Check container logs
docker logs gitea-mcp
# Check container status
docker ps -a | grep gitea-mcp
# Inspect container
docker inspect gitea-mcp
Common Causes:
- Invalid Access Token: Check
GITEA_ACCESS_TOKENin docker-compose.yml - Gitea Host Unreachable: Verify
GITEA_HOSTis accessible from Miranda - Port Conflict: Check if port 25535 is already in use
- Image Pull Failure: Check Docker registry connectivity
Solutions:
# Test Gitea connectivity
curl -I https://gitea.ouranos.helu.ca
# Check port availability
ss -tlnp | grep 25535
# Pull image manually
docker pull docker.gitea.com/gitea-mcp-server:latest
# Re-run deployment with verbose logging
ansible-playbook gitea_mcp/deploy.yml -vv
Authentication Errors
Symptom: "401 Unauthorized" or "403 Forbidden" in logs
Diagnosis:
# Check token is correctly passed
ssh miranda.incus
docker exec gitea-mcp env | grep GITEA_ACCESS_TOKEN
# Test token manually
TOKEN="your_token_here"
curl -H "Authorization: token $TOKEN" https://gitea.ouranos.helu.ca/api/v1/user
Solutions:
- Verify token scopes in Gitea (repo, user, org, issue, pull_request)
- Regenerate token if expired or revoked
- Update vault with new token
- Re-run deployment
Network Connectivity Issues
Symptom: Cannot connect to Gitea or MCP endpoint unreachable
Diagnosis:
# Test Gitea from Miranda
ssh miranda.incus
curl -v https://gitea.ouranos.helu.ca
# Test MCP endpoint from other hosts
curl -v http://miranda.incus:25535
# Check Docker network
docker network inspect bridge
Solutions:
- Verify Miranda can resolve and reach
gitea.ouranos.helu.ca - Check firewall rules on Miranda
- Verify port 25535 is not blocked
- Check Docker network configuration
Logs Not Appearing in Loki
Symptom: No logs in Grafana from gitea-mcp container
Diagnosis:
# Check Alloy is listening on syslog port
ssh miranda.incus
ss -tlnp | grep 51435
# Check Alloy configuration
sudo systemctl status alloy
# Verify syslog driver is configured
docker inspect gitea-mcp | grep -A 10 LogConfig
Solutions:
- Verify Alloy is running:
sudo systemctl status alloy - Check Alloy syslog source configuration
- Verify
gitea_mcp_syslog_portmatches Alloy config - Restart Alloy:
sudo systemctl restart alloy - Restart container to reconnect syslog
Permission Denied Errors
Symptom: Cannot access /srv/gitea_mcp or docker-compose.yml
Diagnosis:
ssh miranda.incus
# Check directory permissions
ls -la /srv/gitea_mcp
# Check user group membership
groups # Should show gitea_mcp group
# Check file ownership
ls -la /srv/gitea_mcp/docker-compose.yml
Solutions:
# Re-run deployment to fix permissions
ansible-playbook gitea_mcp/deploy.yml
# Manually fix if needed
sudo chown -R gitea_mcp:gitea_mcp /srv/gitea_mcp
sudo chmod 750 /srv/gitea_mcp
sudo chmod 550 /srv/gitea_mcp/docker-compose.yml
# Re-login to apply group changes
exit
ssh miranda.incus
MCP Switchboard Integration Issues
Symptom: Switchboard cannot connect to Gitea MCP server
Diagnosis:
# Check switchboard configuration
ssh oberon.incus
cat /srv/mcp-switchboard/config.json | jq '.servers.gitea'
# Test connectivity from Oberon
curl -v http://miranda.incus:25535
Solutions:
- Verify Gitea MCP server URL in switchboard config
- Check network connectivity: Oberon → Miranda
- Verify port 25535 is accessible
- Restart MCP Switchboard after config changes
MCP Protocol Integration
Server Capabilities
The Gitea MCP Server exposes these resources and tools via the MCP protocol:
Resources:
- Repository information
- File contents
- Issue details
- Pull request data
- User profiles
- Organization information
Tools:
list_repositories: List accessible repositoriesget_repository: Get repository detailslist_issues: Search and list issuescreate_issue: Create new issueupdate_issue: Modify existing issuelist_pull_requests: List PRs in repositorycreate_pull_request: Open new PRsearch_code: Search code across repositories
Switchboard Configuration
MCP Switchboard on Oberon routes MCP requests to Gitea MCP Server.
Configuration (/srv/mcp-switchboard/config.json):
{
"servers": {
"gitea": {
"command": null,
"args": [],
"url": "http://miranda.incus:25535",
"transport": "http"
}
}
}
Client Usage
From AI Assistant (Claude Desktop, Cline, etc.):
The assistant can interact with Gitea repositories through natural language:
- "List all repositories in the organization"
- "Show me open issues in the agathos repository"
- "Create an issue about improving documentation"
- "Search for 'ansible' in repository code"
Direct MCP Client:
POST http://oberon.incus:22781/mcp/invoke
Content-Type: application/json
{
"server": "gitea",
"method": "list_repositories",
"params": {}
}
Security Considerations
Access Token Management
Best Practices:
- Store token in Ansible Vault (never in plain text)
- Use minimum required scopes for token
- Rotate tokens periodically
- Revoke tokens when no longer needed
- Use separate tokens for different services
Token Rotation:
# 1. Generate new token in Gitea
# 2. Update vault
ansible-vault edit ansible/inventory/group_vars/all/vault.yml
# 3. Re-deploy to update environment variable
ansible-playbook gitea_mcp/deploy.yml
# 4. Revoke old token in Gitea
Network Security
Isolation:
- Service only accessible within Incus network (10.10.0.0/24)
- No direct external exposure (proxied through Switchboard)
- TLS handled by HAProxy (upstream) for external access
Access Control:
- Gitea enforces user/repository permissions
- MCP protocol authenticated by Switchboard
- Container runs as non-root user
Audit and Monitoring
Logging:
- All requests logged to Loki via syslog
- Grafana dashboards for monitoring access patterns
- Alert on authentication failures
Monitoring Queries:
# All Gitea MCP logs
{job="syslog", container_name="gitea-mcp"}
# Authentication errors
{job="syslog", container_name="gitea-mcp"} |= "401" or |= "403"
# Error rate
rate({job="syslog", container_name="gitea-mcp"} |= "error" [5m])
Performance Considerations
Resource Usage
Container Resources:
- Memory: ~50-100 MB baseline
- CPU: Minimal (< 1% idle, spikes during API calls)
- Disk: ~100 MB for image, minimal runtime storage
Scaling Considerations:
- Single container sufficient for development/sandbox
- For production: Consider multiple replicas behind load balancer
- Gitea API rate limits apply to token (typically 5000 requests/hour)
Optimization
Caching:
- Gitea MCP Server may cache repository metadata
- Restart container to clear cache if needed
Connection Pooling:
- Server maintains connection pool to Gitea API
- Reuses connections for better performance
Related Documentation
Agathos Infrastructure
- Agathos Overview - Complete infrastructure documentation
- Ansible Best Practices - Deployment patterns and structure
- Miranda Host - MCP Docker host details
Related Services
- Gitea Service - Gitea server deployment and configuration
- MCP Switchboard - MCP request routing
- Grafana MCP - Similar MCP server deployment
External References
- Gitea API Documentation - Gitea REST API reference
- Model Context Protocol Specification - MCP protocol details
- Gitea MCP Server Repository - Upstream project
- Docker Compose Documentation - Container orchestration
Maintenance Schedule
Regular Tasks:
- Weekly: Review logs for errors or anomalies
- Monthly: Update container image to latest version
- Quarterly: Rotate Gitea access token
- As Needed: Review and adjust token permissions
Update Procedure:
# Pull latest image and restart
ansible-playbook gitea_mcp/deploy.yml
# Verify new version
ssh miranda.incus
docker inspect gitea-mcp | jq '.[0].Config.Image'
Last Updated: February 2026
Project: Agathos Infrastructure
Host: Miranda (MCP Docker Host)
Status: Red Panda Approved™ ✓