Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94e0a27b36 | |||
| 4eb7f78b6b | |||
| c6ed87cdcf | |||
| f059b6f458 | |||
| 4b350fe967 |
45
README.md
45
README.md
@@ -4,11 +4,14 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that e
|
||||
|
||||
## Features
|
||||
|
||||
- **Organization Management** - List orgs, view overviews, update settings, manage profile READMEs
|
||||
- **Repository Organization** - List repos with group support, pin/unpin featured repos
|
||||
- **Query Runners** - List runners, check status, view capabilities, monitor queue depth
|
||||
- **Manage Workflows** - List, trigger, rerun, cancel, and approve workflow runs
|
||||
- **View Logs** - Get job logs with automatic error extraction for failed jobs
|
||||
- **Access Artifacts** - List and download workflow artifacts
|
||||
- **Manage Releases** - List releases, get assets, check download counts
|
||||
- **Landing Pages** - Configure and customize repository landing pages
|
||||
- **AI Learning** - Query error patterns, report solutions, help other AIs learn
|
||||
- **AI-Friendly** - Structured JSON responses designed for AI consumption
|
||||
|
||||
@@ -52,6 +55,14 @@ Add to your Claude Code settings:
|
||||
### 3. Use It
|
||||
|
||||
Ask Claude things like:
|
||||
- "What organizations do I have access to?"
|
||||
- "Show me the overview for myorg"
|
||||
- "List all repos in myorg grouped by category"
|
||||
- "Update myorg's description and website"
|
||||
- "Show me the profile README for myorg"
|
||||
- "Update the profile README for myorg"
|
||||
- "Pin the 'flagship-app' repo to the myorg overview"
|
||||
- "Unpin 'old-project' from myorg"
|
||||
- "What runners are online?"
|
||||
- "Show me the latest workflow runs for gitcaddy/act_runner"
|
||||
- "Why did run #77 fail?"
|
||||
@@ -73,9 +84,24 @@ Ask Claude things like:
|
||||
- "List all repos for myorg"
|
||||
- "List open issues for gitcaddy/server"
|
||||
- "Show me issue #42 in myorg/myrepo"
|
||||
- "Set up a landing page for myorg/myrepo"
|
||||
- "Update the hero section on the landing page"
|
||||
|
||||
## Available Tools
|
||||
|
||||
### Organization Tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `list_orgs` | List all organizations the authenticated user belongs to |
|
||||
| `get_org_overview` | Get comprehensive org overview: pinned repos, groups, members, stats, profile README, recent activity |
|
||||
| `update_org` | Update org info (full name, email, description, website, location, group header) |
|
||||
| `list_org_repos` | List repos for an org with optional `group_by=group_header` grouping |
|
||||
| `get_org_profile_readme` | Get the raw markdown of an org's profile README from its `.profile` repo |
|
||||
| `update_org_profile_readme` | Update (or create) the org's profile README; creates the `.profile` repo if needed |
|
||||
| `pin_org_repo` | Pin a repository to the org's overview page, optionally into a group |
|
||||
| `unpin_org_repo` | Unpin a repository from the org's overview page |
|
||||
|
||||
### Runner Tools
|
||||
|
||||
| Tool | Description |
|
||||
@@ -134,6 +160,25 @@ Ask Claude things like:
|
||||
| `get_compatibility_matrix` | See what project types work on which runners |
|
||||
| `diagnose_job_failure` | Auto-analyze failed job logs and suggest solutions |
|
||||
|
||||
### Landing Page Tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `get_landing_config` | Get the full landing page configuration for a repository |
|
||||
| `list_landing_templates` | List available landing page templates |
|
||||
| `enable_landing_page` | Enable or disable a repository's landing page |
|
||||
| `update_landing_brand` | Update the brand section (name, logo, colors) |
|
||||
| `update_landing_hero` | Update the hero section (headline, description, CTA) |
|
||||
| `update_landing_pricing` | Update the pricing section |
|
||||
| `update_landing_comparison` | Update the feature comparison table |
|
||||
| `update_landing_features` | Update the features list |
|
||||
| `update_landing_social_proof` | Update testimonials and social proof |
|
||||
| `update_landing_seo` | Update SEO metadata (title, description, keywords) |
|
||||
| `update_landing_theme` | Update the visual theme (colors, fonts, layout) |
|
||||
| `update_landing_stats` | Update stat counters |
|
||||
| `update_landing_value_props` | Update value propositions |
|
||||
| `update_landing_cta` | Update the call-to-action section |
|
||||
|
||||
## AI Learning System
|
||||
|
||||
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:
|
||||
|
||||
41
main.go
41
main.go
@@ -90,10 +90,22 @@ func main() {
|
||||
|
||||
debugLog("Received: %s", string(line))
|
||||
|
||||
// JSON-RPC notifications (no "id" field) must NEVER receive a response.
|
||||
// The MCP spec defines notifications/initialized, notifications/cancelled,
|
||||
// notifications/progress, etc. — these are fire-and-forget. Even if the
|
||||
// remote gitcaddy server returns an error for an unknown notification, we
|
||||
// must suppress it on the way back to the client (Claude Code), otherwise
|
||||
// the client sees an unexpected response and fails the handshake.
|
||||
isNotification := isJsonRpcNotification(line)
|
||||
|
||||
// Forward to Gitea's MCP endpoint
|
||||
response, err := forwardToGitea(line)
|
||||
if err != nil {
|
||||
debugLog("Forward error: %v", err)
|
||||
if isNotification {
|
||||
// Notification — no response allowed, just swallow.
|
||||
continue
|
||||
}
|
||||
// Send error response
|
||||
errorResp := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -110,11 +122,30 @@ func main() {
|
||||
|
||||
debugLog("Response: %s", string(response))
|
||||
|
||||
// Write response to stdout
|
||||
fmt.Println(string(response))
|
||||
if isNotification {
|
||||
// Notification — never respond, even if the remote sent something.
|
||||
debugLog("Suppressing response to notification")
|
||||
continue
|
||||
}
|
||||
|
||||
// Write response to stdout as newline-delimited JSON
|
||||
writeFramed(response)
|
||||
}
|
||||
}
|
||||
|
||||
// isJsonRpcNotification reports whether a raw JSON-RPC message is a notification
|
||||
// (a request without an "id" field). Per JSON-RPC 2.0, servers MUST NOT reply to
|
||||
// notifications.
|
||||
func isJsonRpcNotification(raw []byte) bool {
|
||||
var probe map[string]json.RawMessage
|
||||
if err := json.Unmarshal(raw, &probe); err != nil {
|
||||
return false
|
||||
}
|
||||
_, hasId := probe["id"]
|
||||
_, hasMethod := probe["method"]
|
||||
return hasMethod && !hasId
|
||||
}
|
||||
|
||||
func forwardToGitea(request []byte) ([]byte, error) {
|
||||
mcpURL := giteaURL + "/api/v2/mcp"
|
||||
|
||||
@@ -211,7 +242,11 @@ func readMessage(reader *bufio.Reader) ([]byte, error) {
|
||||
|
||||
func writeResponse(resp interface{}) {
|
||||
data, _ := json.Marshal(resp)
|
||||
fmt.Println(string(data))
|
||||
writeFramed(data)
|
||||
}
|
||||
|
||||
func writeFramed(data []byte) {
|
||||
fmt.Fprintf(os.Stdout, "Content-Length: %d\r\n\r\n%s", len(data), data)
|
||||
}
|
||||
|
||||
func debugLog(format string, args ...interface{}) {
|
||||
|
||||
Reference in New Issue
Block a user