feat: gadfly-mcp — MCP server for grading gadfly-reports findings
CI / test (push) Successful in 10m10s

Thin, stateless stdio MCP server (official Go SDK) that exposes a gadfly-reports store to an MCP client (e.g. Claude). Tools: list_findings, record_finding_grade, scoreboard (grader forced to claude). Launch via 'go run ...@latest' — nothing to install. Core logic tested against httptest, no daemon required.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-26 23:55:24 -04:00
parent bb6bd209b4
commit f92e54e3ed
9 changed files with 730 additions and 28 deletions
+50
View File
@@ -0,0 +1,50 @@
# gadfly-mcp — Developer Guide
A stdio [MCP](https://modelcontextprotocol.io) server exposing the
[gadfly-reports](https://gitea.stevedudenhoeffer.com/steve/gadfly-reports) findings store to an MCP
client (e.g. Claude). It is a **thin, stateless HTTP client** to the store — it never opens SQLite
and never imports the store's package.
> This is a public, **vibe-coded** project (built largely by an AI agent). Keep that honest in the
> README; it's homelab-grade.
## Shape
- Single `main.go` (`package main`) at the repo root, so the launch path is just
`go run gitea.stevedudenhoeffer.com/steve/gadfly-mcp@latest` — no `cmd/` subpath. This is the
whole point: the client compiles + caches it on demand; nothing to install or manage.
- Uses the official Go MCP SDK (`github.com/modelcontextprotocol/go-sdk`): `mcp.NewServer`
`mcp.AddTool[In,Out]` (input schemas inferred from struct + `jsonschema` tags) → `server.Run(ctx,
&mcp.StdioTransport{})`.
- Config: `--store` flag (default `$GADFLY_REPORTS_URL`, else `http://localhost:8090`); bearer token
from `$GADFLY_REPORTS_TOKEN`, sent on every request when set.
## Contract with gadfly-reports
The store's HTTP/JSON API is the contract — its README is the **source of truth**. This client
mirrors only the subset it needs with small local structs (`exportRow`, `modelStat`, `gradeReq`,
…). If you change a field here, check it against gadfly-reports' `server.go`/`store.go`. Endpoints
used: `GET /export`, `POST /findings/{id}/grade`, `GET /scoreboard`.
Three tools: `list_findings`, `record_finding_grade`, `scoreboard`. The grader is always forced to
`"claude"`. The store holds **no points**; ranking by points/value-per-minute is a client concern —
say so in the `scoreboard` tool description.
## Tests
The core logic (`groupFindings` / `listFindings` / `recordGrade` / `scoreboard`) is factored free of
MCP types and tested against an `httptest.Server`, so tests need no real daemon. Keep it that way —
add a test when you add a tool or change the grouping/filtering.
```sh
go build ./...
go vet ./...
gofmt -l . # must be empty
go test -race ./...
```
## When making changes
- Keep this a thin client: no SQLite, no business logic the store should own.
- Keep the launch path a root `package main` (don't move it under `cmd/`), so `go run …@latest`
stays the one-liner the README documents.