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>
2.3 KiB
gadfly-mcp — Developer Guide
A stdio MCP server exposing the 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 justgo run gitea.stevedudenhoeffer.com/steve/gadfly-mcp@latest— nocmd/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 +jsonschematags) →server.Run(ctx, &mcp.StdioTransport{}). - Config:
--storeflag (default$GADFLY_REPORTS_URL, elsehttp://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.
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 undercmd/), sogo run …@lateststays the one-liner the README documents.