2
0

9 Commits

Author SHA1 Message Date
f059b6f458 fix(mcp): use NDJSON instead of LSP framing for MCP stdio
All checks were successful
Release / build (amd64, windows) (push) Successful in 43s
Release / build (arm64, linux) (push) Successful in 1m0s
Release / build (amd64, linux) (push) Successful in 1m10s
Release / build (amd64, darwin) (push) Successful in 1m19s
Release / build (arm64, darwin) (push) Successful in 1m46s
Release / release (push) Successful in 21s
Replace Content-Length framing with newline-delimited JSON (NDJSON) for MCP stdio transport. MCP clients like Claude Code expect one JSON object per line, not LSP-style Content-Length headers. The previous framing caused clients to reject responses.
2026-04-15 02:20:20 -04:00
4b350fe967 fix(mcp): use Content-Length framing for all responses
All checks were successful
Release / build (amd64, linux) (push) Successful in 38s
Release / build (amd64, windows) (push) Successful in 38s
Release / build (arm64, darwin) (push) Successful in 44s
Release / build (amd64, darwin) (push) Successful in 1m4s
Release / build (arm64, linux) (push) Successful in 1m14s
Release / release (push) Successful in 21s
Adds writeFramed helper to send responses with HTTP-style Content-Length headers instead of raw JSON lines. Ensures compatibility with Claude Code which expects framed messages. Updates both main loop and writeResponse to use consistent framing.
2026-04-05 02:48:24 -04:00
1d770b45c9 feat(mcp): add support for Content-Length framed JSON-RPC messages
All checks were successful
Release / build (amd64, windows) (push) Successful in 41s
Release / build (arm64, darwin) (push) Successful in 42s
Release / build (amd64, linux) (push) Successful in 46s
Release / build (arm64, linux) (push) Successful in 1m11s
Release / release (push) Successful in 19s
Release / build (amd64, darwin) (push) Successful in 1m4s
Implements readMessage function to handle both raw JSON lines and Content-Length framed messages. Claude Code uses HTTP-style framing (Content-Length: N\r\n\r\n{json}) while other clients may send raw JSON. Auto-detects format by peeking at first bytes. Adds .gitignore for compiled binaries.
2026-04-05 02:24:17 -04:00
fd473c298c docs: add repository and issue tools to readme
All checks were successful
Release / build (amd64, linux) (push) Successful in 30s
Release / build (amd64, windows) (push) Successful in 31s
Release / build (arm64, darwin) (push) Successful in 34s
Release / build (arm64, linux) (push) Successful in 41s
Release / build (amd64, darwin) (push) Successful in 53s
Release / release (push) Successful in 14s
2026-03-06 19:23:37 -05:00
44f75cf6e0 docs(ci): add workflow validation tool to documentation
- Document new validate_workflow tool in examples and tool table
- Update list_workflows description to mention validation status
- Add example usage for workflow validation
2026-02-01 06:46:28 -05:00
01da7b9736 docs(ci): add example query for listing repositories 2026-01-26 01:30:49 -05:00
57a46d9bf8 docs(ci): add package defaults tool to readme 2026-01-25 22:39:30 -05:00
b1709b47cd docs(api): document new workflow management and artifact tools
All checks were successful
Release / build (amd64, windows) (push) Successful in 52s
Release / build (amd64, darwin) (push) Successful in 37s
Release / build (amd64, linux) (push) Successful in 35s
Release / build (arm64, darwin) (push) Successful in 36s
Release / build (arm64, linux) (push) Successful in 43s
Release / release (push) Successful in 45s
Add documentation for new MCP tools: trigger_workflow, rerun_workflow, cancel_workflow_run, approve_workflow, list_workflows, get_workflow_file, list_artifacts, get_artifact_download_url, and get_queue_depth. Reorganize tools section into logical categories (Runner, Workflow, Artifact, Release & Package) and add usage examples.
2026-01-25 15:41:39 -05:00
2ec5ed0ff7 docs(mcp): add list_secrets and list_packages to MCP tools
All checks were successful
Release / build (amd64, darwin) (push) Successful in 40s
Release / build (amd64, linux) (push) Successful in 55s
Release / build (amd64, windows) (push) Successful in 47s
Release / build (arm64, darwin) (push) Successful in 49s
Release / build (arm64, linux) (push) Successful in 42s
Release / release (push) Successful in 17s
Updates README with documentation for new list_secrets and list_packages MCP tools. Adds example queries showing how to use these tools with Claude.
2026-01-24 15:05:32 -05:00
3 changed files with 134 additions and 8 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.exe
*.dll
*.so
*.dylib

View File

@@ -4,8 +4,10 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that e
## Features ## Features
- **Query Runners** - List runners, check status, view capabilities (OS, tools, disk space) - **Query Runners** - List runners, check status, view capabilities, monitor queue depth
- **Monitor Workflows** - List runs, get job details, view logs - **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 - **Manage Releases** - List releases, get assets, check download counts
- **AI Learning** - Query error patterns, report solutions, help other AIs learn - **AI Learning** - Query error patterns, report solutions, help other AIs learn
- **AI-Friendly** - Structured JSON responses designed for AI consumption - **AI-Friendly** - Structured JSON responses designed for AI consumption
@@ -53,23 +55,74 @@ Ask Claude things like:
- "What runners are online?" - "What runners are online?"
- "Show me the latest workflow runs for gitcaddy/act_runner" - "Show me the latest workflow runs for gitcaddy/act_runner"
- "Why did run #77 fail?" - "Why did run #77 fail?"
- "Rerun the failed jobs in run #77"
- "Cancel run #80"
- "Trigger the build.yml workflow on the main branch"
- "What workflows are available in myorg/myrepo?"
- "Show me the build.yml workflow file"
- "Validate the build.yml workflow in myorg/myrepo"
- "What artifacts were produced by run #77?"
- "What's the queue depth for each runner label?"
- "Approve the workflow run from the fork PR"
- "What assets are in the v0.3.6 release?" - "What assets are in the v0.3.6 release?"
- "Are there any known solutions for NETSDK1147?" - "Are there any known solutions for NETSDK1147?"
- "Diagnose why job 456 failed" - "Diagnose why job 456 failed"
- "What secrets are available for myorg/myrepo?"
- "List all NuGet packages for myorg"
- "What are the package defaults for myorg?"
- "List all repos for myorg"
- "List open issues for gitcaddy/server"
- "Show me issue #42 in myorg/myrepo"
## Available Tools ## Available Tools
### Runner & Workflow Tools ### Runner Tools
| Tool | Description | | Tool | Description |
|------|-------------| |------|-------------|
| `list_runners` | List all runners with status, capabilities, disk space | | `list_runners` | List all runners with status, capabilities, disk space |
| `get_runner` | Get detailed runner info by ID | | `get_runner` | Get detailed runner info by ID |
| `get_queue_depth` | Get waiting jobs per runner label (capacity insight) |
### Workflow Tools
| Tool | Description |
|------|-------------|
| `list_workflows` | List available workflow files in a repository (includes validation status) |
| `get_workflow_file` | Get the YAML content of a workflow file |
| `validate_workflow` | Validate a workflow YAML file for parse errors (from repo or raw content) |
| `list_workflow_runs` | List workflow runs for a repository | | `list_workflow_runs` | List workflow runs for a repository |
| `get_workflow_run` | Get run details with all jobs | | `get_workflow_run` | Get run details with all jobs |
| `get_job_logs` | Get logs from a specific job | | `get_job_logs` | Get logs from a specific job (auto-extracts errors for failed jobs) |
| `trigger_workflow` | Manually trigger a workflow_dispatch workflow with inputs |
| `rerun_workflow` | Rerun a completed workflow or specific failed job |
| `cancel_workflow_run` | Cancel a running workflow and all its jobs |
| `approve_workflow` | Approve a workflow run that requires approval (fork PRs) |
### Artifact Tools
| Tool | Description |
|------|-------------|
| `list_artifacts` | List artifacts from a workflow run |
| `get_artifact_download_url` | Get the download URL for a specific artifact |
### Repository & Issue Tools
| Tool | Description |
|------|-------------|
| `list_repos` | List repositories for an owner (org or user) |
| `list_issues` | List issues for a repository with pagination and state filtering |
| `get_issue` | Get issue details including body content and comments |
### Release & Package Tools
| Tool | Description |
|------|-------------|
| `list_releases` | List releases for a repository | | `list_releases` | List releases for a repository |
| `get_release` | Get release details with all assets | | `get_release` | Get release details with all assets |
| `list_secrets` | List secret names and descriptions (not values) for global, org, and repo scopes |
| `list_packages` | List packages for an owner or globally with version and visibility info |
| `get_package_defaults` | Get preconfigured package defaults (authors, company, copyright, icon, URLs) for an org |
### AI Learning Tools ### AI Learning Tools

77
main.go
View File

@@ -69,10 +69,11 @@ func main() {
debugLog("Connecting to: %s", giteaURL) debugLog("Connecting to: %s", giteaURL)
// Read JSON-RPC messages from stdin, forward to Gitea, write responses to stdout // Read JSON-RPC messages from stdin, forward to Gitea, write responses to stdout
// Supports both raw JSON lines and Content-Length framed messages (Claude Code uses framing)
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
for { for {
line, err := reader.ReadBytes('\n') line, err := readMessage(reader)
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
debugLog("EOF received, exiting") debugLog("EOF received, exiting")
@@ -109,8 +110,8 @@ func main() {
debugLog("Response: %s", string(response)) debugLog("Response: %s", string(response))
// Write response to stdout // Write response to stdout with Content-Length framing
fmt.Println(string(response)) writeFramed(response)
} }
} }
@@ -147,9 +148,77 @@ func forwardToGitea(request []byte) ([]byte, error) {
return body, nil return body, nil
} }
// readMessage reads a JSON-RPC message from stdin.
// Handles both raw JSON lines and Content-Length framed messages.
// Claude Code sends: Content-Length: N\r\n\r\n{json}
func readMessage(reader *bufio.Reader) ([]byte, error) {
// Peek at first bytes to detect format
for {
line, err := reader.ReadBytes('\n')
if err != nil {
return nil, err
}
trimmed := bytes.TrimSpace(line)
if len(trimmed) == 0 {
continue
}
// If it starts with '{', it's a raw JSON line
if trimmed[0] == '{' {
return trimmed, nil
}
// If it starts with "Content-Length:", read the framed message
if bytes.HasPrefix(bytes.ToLower(trimmed), []byte("content-length:")) {
// Parse content length
parts := bytes.SplitN(trimmed, []byte(":"), 2)
if len(parts) != 2 {
continue
}
lengthStr := bytes.TrimSpace(parts[1])
var contentLength int
fmt.Sscanf(string(lengthStr), "%d", &contentLength)
if contentLength <= 0 {
continue
}
// Read until empty line (end of headers)
for {
headerLine, err := reader.ReadBytes('\n')
if err != nil {
return nil, err
}
if len(bytes.TrimSpace(headerLine)) == 0 {
break
}
}
// Read exactly contentLength bytes
body := make([]byte, contentLength)
_, err := io.ReadFull(reader, body)
if err != nil {
return nil, fmt.Errorf("read body: %w", err)
}
return body, nil
}
// Unknown line, skip
debugLog("Skipping unknown line: %s", string(trimmed))
}
}
func writeResponse(resp interface{}) { func writeResponse(resp interface{}) {
data, _ := json.Marshal(resp) data, _ := json.Marshal(resp)
fmt.Println(string(data)) writeFramed(data)
}
func writeFramed(data []byte) {
// MCP stdio transport uses newline-delimited JSON (NDJSON), not LSP-style
// Content-Length framing. Claude Code and other MCP clients reject
// Content-Length headers on stdio, so we emit one JSON object per line.
fmt.Fprintf(os.Stdout, "%s\n", data)
} }
func debugLog(format string, args ...interface{}) { func debugLog(format string, args ...interface{}) {