2
0

feat: Rebrand to GitCaddy and add AI learning tools docs
Some checks failed
Release / build (arm64, linux) (push) Has been cancelled
Release / release (push) Has been cancelled
Release / build (arm64, darwin) (push) Has been cancelled
Release / build (amd64, darwin) (push) Has been cancelled
Release / build (amd64, linux) (push) Has been cancelled
Release / build (amd64, windows) (push) Has been cancelled

This commit is contained in:
2026-01-14 17:56:12 +00:00
parent 970c02c877
commit 0e649775de
2 changed files with 347 additions and 286 deletions

321
README.md
View File

@@ -1,133 +1,188 @@
# Gitea MCP Server # GitCaddy MCP Server
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that enables AI assistants like Claude to interact directly with your Gitea instance. A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that enables AI assistants like Claude to interact directly with your GitCaddy instance.
## Features ## Features
- **Query Runners** - List runners, check status, view capabilities (OS, tools, disk space) - **Query Runners** - List runners, check status, view capabilities (OS, tools, disk space)
- **Monitor Workflows** - List runs, get job details, view logs - **Monitor Workflows** - List runs, get job details, view logs
- **Manage Releases** - List releases, get assets, check download counts - **Manage Releases** - List releases, get assets, check download counts
- **AI-Friendly** - Structured JSON responses designed for AI consumption - **AI Learning** - Query error patterns, report solutions, help other AIs learn
- **AI-Friendly** - Structured JSON responses designed for AI consumption
## Quick Start
## Quick Start
### 1. Download
### 1. Download
Download the latest binary from [Releases](https://git.marketally.com/gitcaddy/mcp-server/releases).
Download the latest binary from [Releases](https://git.marketally.com/gitcaddy/mcp-server/releases).
### 2. Configure Claude Code
### 2. Configure Claude Code
Add to your Claude Code settings:
Add to your Claude Code settings:
**Option A: Command line arguments**
```json **Option A: Command line arguments**
{ ```json
"mcpServers": { {
"gitea": { "mcpServers": {
"command": "/path/to/gitea-mcp-server", "gitcaddy": {
"args": ["--url", "https://git.marketally.com", "--token", "YOUR_API_TOKEN"] "command": "/path/to/gitcaddy-mcp-server",
} "args": ["--url", "https://git.marketally.com", "--token", "YOUR_API_TOKEN"]
} }
} }
``` }
```
**Option B: Environment variables**
```json **Option B: Environment variables**
{ ```json
"mcpServers": { {
"gitea": { "mcpServers": {
"command": "/path/to/gitea-mcp-server", "gitcaddy": {
"env": { "command": "/path/to/gitcaddy-mcp-server",
"GITEA_URL": "https://git.marketally.com", "env": {
"GITEA_TOKEN": "your-token-here" "GITCADDY_URL": "https://git.marketally.com",
} "GITCADDY_TOKEN": "your-token-here"
} }
} }
} }
``` }
```
### 3. Use It
### 3. Use It
Ask Claude things like:
- "What runners are online?" Ask Claude things like:
- "Show me the latest workflow runs for gitcaddy/act_runner" - "What runners are online?"
- "Why did run #77 fail?" - "Show me the latest workflow runs for gitcaddy/act_runner"
- "What assets are in the v0.3.6 release?" - "Why did run #77 fail?"
- "What assets are in the v0.3.6 release?"
## Available Tools - "Are there any known solutions for NETSDK1147?"
- "Diagnose why job 456 failed"
| Tool | Description |
|------|-------------| ## Available Tools
| `list_runners` | List all runners with status, capabilities, disk space |
| `get_runner` | Get detailed runner info by ID | ### Runner & Workflow Tools
| `list_workflow_runs` | List workflow runs for a repository |
| `get_workflow_run` | Get run details with all jobs | | Tool | Description |
| `get_job_logs` | Get logs from a specific job | |------|-------------|
| `list_releases` | List releases for a repository | | `list_runners` | List all runners with status, capabilities, disk space |
| `get_release` | Get release details with all assets | | `get_runner` | Get detailed runner info by ID |
| `list_workflow_runs` | List workflow runs for a repository |
## Building from Source | `get_workflow_run` | Get run details with all jobs |
| `get_job_logs` | Get logs from a specific job |
```bash | `list_releases` | List releases for a repository |
git clone https://git.marketally.com/gitcaddy/mcp-server.git | `get_release` | Get release details with all assets |
cd mcp-server
go build -o gitea-mcp-server . ### AI Learning Tools
```
| Tool | Description |
### Cross-compile for all platforms |------|-------------|
| `get_error_patterns` | Query known CI/CD error patterns with diagnoses and solutions |
```bash | `report_error_solution` | Report a solution that fixed an error (helps other AIs learn) |
# Linux | `report_solution_success` | Mark a solution as successful (improves ranking) |
GOOS=linux GOARCH=amd64 go build -o gitea-mcp-server-linux-amd64 . | `get_compatibility_matrix` | See what project types work on which runners |
| `diagnose_job_failure` | Auto-analyze failed job logs and suggest solutions |
# macOS Intel
GOOS=darwin GOARCH=amd64 go build -o gitea-mcp-server-darwin-amd64 . ## AI Learning System
# macOS Apple Silicon GitCaddy includes a collaborative AI learning system. When you encounter and fix CI/CD errors, you can report your solutions to help other AI assistants:
GOOS=darwin GOARCH=arm64 go build -o gitea-mcp-server-darwin-arm64 .
```
# Windows AI encounters error -> get_error_patterns -> finds solution -> applies fix
GOOS=windows GOARCH=amd64 go build -o gitea-mcp-server-windows-amd64.exe . |
``` v
report_solution_success
## Architecture (improves ranking)
``` AI finds NEW solution -> report_error_solution -> stored in database
┌─────────────┐ stdio ┌──────────────────┐ HTTP ┌─────────────┐ |
│ Claude Code │ ◄────────────► │ gitea-mcp-server │ ◄───────────► │ Gitea │ v
(AI Tool) JSON-RPC (This binary) /api/v2/mcp Server │ Other AIs can now find it
└─────────────┘ └──────────────────┘ └─────────────┘ ```
```
### Example: Diagnosing a Failed Build
The MCP server:
1. Receives JSON-RPC requests over stdio from Claude Code ```
2. Forwards them to Gitea's `/api/v2/mcp` endpoint User: "Why did job 789 fail?"
3. Returns responses back to Claude Code
Claude uses: diagnose_job_failure(owner="myorg", repo="myapp", job_id=789)
## Configuration Options
Response:
| Flag | Env Variable | Description | {
|------|-------------|-------------| "extracted_errors": ["NETSDK1147", "XA5300"],
| `--url` | `GITEA_URL` | Gitea server URL (required) | "diagnoses": [
| `--token` | `GITEA_TOKEN` | API token for authentication | {
| `--debug` | - | Enable debug logging to stderr | "pattern": "NETSDK1147",
"diagnosis": "The .NET MAUI Android workload is not installed",
## Obtaining an API Token "solution": "Install the workload: dotnet workload install maui-android",
"success_rate": 94.5
1. Go to your Gitea instance → Settings → Applications }
2. Generate a new token with appropriate scopes ]
3. Copy the token and use it in your configuration }
```
## Requirements
## Building from Source
- Gitea 1.26+ with GitCaddy enhancements (includes `/api/v2/mcp` endpoint)
- Go 1.21+ (for building from source) ```bash
git clone https://git.marketally.com/gitcaddy/mcp-server.git
## License cd mcp-server
go build -o gitcaddy-mcp-server .
MIT License - See [LICENSE](LICENSE) file. ```
## Related Projects ### Cross-compile for all platforms
- [GitCaddy Gitea](https://git.marketally.com/gitcaddy/gitea) - Enhanced Gitea fork with AI-friendly APIs ```bash
- [GitCaddy act_runner](https://git.marketally.com/gitcaddy/act_runner) - Enhanced runner with capabilities detection # Linux
GOOS=linux GOARCH=amd64 go build -o gitcaddy-mcp-server-linux-amd64 .
# macOS Intel
GOOS=darwin GOARCH=amd64 go build -o gitcaddy-mcp-server-darwin-amd64 .
# macOS Apple Silicon
GOOS=darwin GOARCH=arm64 go build -o gitcaddy-mcp-server-darwin-arm64 .
# Windows
GOOS=windows GOARCH=amd64 go build -o gitcaddy-mcp-server-windows-amd64.exe .
```
## Architecture
```
+-------------+ stdio +------------------+ HTTP +-------------+
| Claude Code | <------------> | gitcaddy-mcp | <-----------> | GitCaddy |
| (AI Tool) | JSON-RPC | (This binary) | /api/v2/mcp | Server |
+-------------+ +------------------+ +-------------+
```
The MCP server:
1. Receives JSON-RPC requests over stdio from Claude Code
2. Forwards them to GitCaddy's `/api/v2/mcp` endpoint
3. Returns responses back to Claude Code
## Configuration Options
| Flag | Env Variable | Description |
|------|-------------|-------------|
| `--url` | `GITCADDY_URL` | GitCaddy server URL (required) |
| `--token` | `GITCADDY_TOKEN` | API token for authentication |
| `--debug` | - | Enable debug logging to stderr |
**Note:** `GITEA_URL` and `GITEA_TOKEN` are also supported for backwards compatibility.
## Obtaining an API Token
1. Go to your GitCaddy instance -> Settings -> Applications
2. Generate a new token with appropriate scopes
3. Copy the token and use it in your configuration
## Requirements
- GitCaddy Server (Gitea 1.26+ with GitCaddy enhancements)
- Go 1.21+ (for building from source)
## License
MIT License - See [LICENSE](LICENSE) file.
## Related Projects
- [GitCaddy Server](https://git.marketally.com/gitcaddy/gitea) - Enhanced Gitea fork with AI-friendly APIs
- [GitCaddy Runner](https://git.marketally.com/gitcaddy/act_runner) - Enhanced runner with capabilities detection

312
main.go
View File

@@ -1,153 +1,159 @@
// Copyright 2026 MarketAlly. All rights reserved. // Copyright 2026 MarketAlly. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Gitea MCP Server - Model Context Protocol server for Gitea Actions // GitCaddy MCP Server - Model Context Protocol server for GitCaddy/Gitea Actions
// //
// This standalone server implements the MCP protocol over stdio, // This standalone server implements the MCP protocol over stdio,
// proxying requests to a Gitea instance's /api/v2/mcp endpoint. // proxying requests to a GitCaddy instance's /api/v2/mcp endpoint.
// //
// Usage: // Usage:
// //
// gitea-mcp-server --url https://git.example.com --token YOUR_API_TOKEN // gitcaddy-mcp-server --url https://git.example.com --token YOUR_API_TOKEN
// //
// Configure in Claude Code's settings.json: // Configure in Claude Code's settings.json:
// //
// { // {
// "mcpServers": { // "mcpServers": {
// "gitea": { // "gitcaddy": {
// "command": "gitea-mcp-server", // "command": "gitcaddy-mcp-server",
// "args": ["--url", "https://git.example.com", "--token", "YOUR_TOKEN"] // "args": ["--url", "https://git.example.com", "--token", "YOUR_TOKEN"]
// } // }
// } // }
// } // }
package main package main
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os" "os"
"time" "time"
) )
var ( var (
giteaURL string giteaURL string
giteaToken string giteaToken string
debug bool debug bool
) )
func main() { func main() {
flag.StringVar(&giteaURL, "url", "", "Gitea server URL (e.g., https://git.example.com)") flag.StringVar(&giteaURL, "url", "", "GitCaddy server URL (e.g., https://git.example.com)")
flag.StringVar(&giteaToken, "token", "", "Gitea API token") flag.StringVar(&giteaToken, "token", "", "GitCaddy API token")
flag.BoolVar(&debug, "debug", false, "Enable debug logging to stderr") flag.BoolVar(&debug, "debug", false, "Enable debug logging to stderr")
flag.Parse() flag.Parse()
// Also check environment variables // Check environment variables (GITCADDY_* preferred, GITEA_* for backwards compat)
if giteaURL == "" { if giteaURL == "" {
giteaURL = os.Getenv("GITEA_URL") giteaURL = os.Getenv("GITCADDY_URL")
} }
if giteaToken == "" { if giteaURL == "" {
giteaToken = os.Getenv("GITEA_TOKEN") giteaURL = os.Getenv("GITEA_URL")
} }
if giteaToken == "" {
if giteaURL == "" { giteaToken = os.Getenv("GITCADDY_TOKEN")
fmt.Fprintln(os.Stderr, "Error: --url or GITEA_URL is required") }
os.Exit(1) if giteaToken == "" {
} giteaToken = os.Getenv("GITEA_TOKEN")
}
debugLog("Gitea MCP Server starting")
debugLog("Connecting to: %s", giteaURL) if giteaURL == "" {
fmt.Fprintln(os.Stderr, "Error: --url or GITCADDY_URL is required")
// Read JSON-RPC messages from stdin, forward to Gitea, write responses to stdout os.Exit(1)
reader := bufio.NewReader(os.Stdin) }
for { debugLog("GitCaddy MCP Server starting")
line, err := reader.ReadBytes('\n') debugLog("Connecting to: %s", giteaURL)
if err != nil {
if err == io.EOF { // Read JSON-RPC messages from stdin, forward to Gitea, write responses to stdout
debugLog("EOF received, exiting") reader := bufio.NewReader(os.Stdin)
break
} for {
debugLog("Read error: %v", err) line, err := reader.ReadBytes('\n')
continue if err != nil {
} if err == io.EOF {
debugLog("EOF received, exiting")
line = bytes.TrimSpace(line) break
if len(line) == 0 { }
continue debugLog("Read error: %v", err)
} continue
}
debugLog("Received: %s", string(line))
line = bytes.TrimSpace(line)
// Forward to Gitea's MCP endpoint if len(line) == 0 {
response, err := forwardToGitea(line) continue
if err != nil { }
debugLog("Forward error: %v", err)
// Send error response debugLog("Received: %s", string(line))
errorResp := map[string]interface{}{
"jsonrpc": "2.0", // Forward to Gitea's MCP endpoint
"id": nil, response, err := forwardToGitea(line)
"error": map[string]interface{}{ if err != nil {
"code": -32603, debugLog("Forward error: %v", err)
"message": "Internal error", // Send error response
"data": err.Error(), errorResp := map[string]interface{}{
}, "jsonrpc": "2.0",
} "id": nil,
writeResponse(errorResp) "error": map[string]interface{}{
continue "code": -32603,
} "message": "Internal error",
"data": err.Error(),
debugLog("Response: %s", string(response)) },
}
// Write response to stdout writeResponse(errorResp)
fmt.Println(string(response)) continue
} }
}
debugLog("Response: %s", string(response))
func forwardToGitea(request []byte) ([]byte, error) {
mcpURL := giteaURL + "/api/v2/mcp" // Write response to stdout
fmt.Println(string(response))
req, err := http.NewRequest("POST", mcpURL, bytes.NewReader(request)) }
if err != nil { }
return nil, fmt.Errorf("create request: %w", err)
} func forwardToGitea(request []byte) ([]byte, error) {
mcpURL := giteaURL + "/api/v2/mcp"
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json") req, err := http.NewRequest("POST", mcpURL, bytes.NewReader(request))
if giteaToken != "" { if err != nil {
req.Header.Set("Authorization", "token "+giteaToken) return nil, fmt.Errorf("create request: %w", err)
} }
client := &http.Client{Timeout: 30 * time.Second} req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req) req.Header.Set("Accept", "application/json")
if err != nil { if giteaToken != "" {
return nil, fmt.Errorf("http request: %w", err) req.Header.Set("Authorization", "token "+giteaToken)
} }
defer resp.Body.Close()
client := &http.Client{Timeout: 30 * time.Second}
body, err := io.ReadAll(resp.Body) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("read response: %w", err) return nil, fmt.Errorf("http request: %w", err)
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("http status %d: %s", resp.StatusCode, string(body)) body, err := io.ReadAll(resp.Body)
} if err != nil {
return nil, fmt.Errorf("read response: %w", err)
return body, nil }
}
if resp.StatusCode != http.StatusOK {
func writeResponse(resp interface{}) { return nil, fmt.Errorf("http status %d: %s", resp.StatusCode, string(body))
data, _ := json.Marshal(resp) }
fmt.Println(string(data))
} return body, nil
}
func debugLog(format string, args ...interface{}) {
if debug { func writeResponse(resp interface{}) {
fmt.Fprintf(os.Stderr, "[DEBUG] "+format+"\n", args...) data, _ := json.Marshal(resp)
} fmt.Println(string(data))
} }
func debugLog(format string, args ...interface{}) {
if debug {
fmt.Fprintf(os.Stderr, "[DEBUG] "+format+"\n", args...)
}
}