Skip to content

Set up a dev environment & contribute

Contributions are welcome. This guide covers setting up your development environment, running the test suite, and submitting a pull request.

  • Go 1.26+ — install from go.dev/dl.
  • golangci-lint v2.11 — the linter version pinned for this project.

Install golangci-lint to ~/go/bin/ (the go install path does not work for v2.x — use the install script):

Terminal window
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
| sh -s -- -b ~/go/bin v2.11.0

Verify:

Terminal window
~/go/bin/golangci-lint --version
# golangci-lint has version v2.11.x
Terminal window
git clone https://github.com/lmgarret/polar-flow-mcp.git
cd polar-flow-mcp
make build

The binary is written to bin/polar-flow-mcp. The build uses CGO_ENABLED=0 so no C toolchain is required.

Terminal window
make test

This runs:

Terminal window
CGO_ENABLED=0 go test -tags=polartest -race -count=1 ./...

The -tags=polartest flag is required — it enables test-only code that allows redirecting HTTP requests to test servers. Omitting the flag will cause test failures. All tests must pass before submitting a PR; CI runs the same command.

Terminal window
make lint

This runs:

Terminal window
~/go/bin/golangci-lint run ./...

The linter is configured in .golangci.yml at the repo root. Enabled linters include gocyclo, godot, misspell, noctx, and errcheck (with type assertion checking). Fix all lint errors before submitting a PR.

  • make test passes
  • make lint passes with zero errors
  • Documentation updated if you changed behaviour or added a feature
  • Commit messages follow conventional commit format (see below)

This project uses Conventional Commits:

<type>(<scope>): <short description>
[optional body]
TypeWhen to use
featNew feature or new MCP tool
fixBug fix
docsDocumentation only
choreMaintenance (deps, config)
refactorCode restructuring without behaviour change
testTest additions or changes
ciCI workflow changes

Examples:

feat(mcp): add list_training_targets tool
fix(oauth): handle expired CSRF state gracefully
docs(security): document key rotation plan
chore(deps): update mcp-go to v0.8.0

Conventional commits feed the release notes. Pushing a v* tag runs the release workflow, which uses an LLM to summarise the commits since the previous tag into categorised, benefit-focused notes and publishes them on the GitHub Release (alongside the Docker image ladder and the full commit log). Clear, well-scoped commit messages therefore produce clearer release notes.

To run the server locally for development you need a .env file (or exported variables) with at minimum:

Terminal window
export POLAR_EMAIL=you+polartest@example.com
export POLAR_PASSWORD=correct-horse-battery-staple
export COOKIE_JAR_PATH=./polar-cookies-dev.json
export LOG_LEVEL=debug

Then:

Terminal window
make build
./bin/polar-flow-mcp

The server binds to 127.0.0.1:8080 by default. Liveness check:

Terminal window
curl http://127.0.0.1:8080/healthz

On the first run the server performs the headless login chain against auth.polar.com and writes polar-cookies-dev.json (mode 0600). Subsequent runs re-use the jar and skip the password step.

If you bump the upstream spec or the ogen version, regenerate internal/flow/gen/:

Terminal window
python3 internal/flow/preprocess-spec.py \
../polar-openapi-maker/dist/openapi.yaml \
internal/flow/openapi.yaml
go install github.com/ogen-go/ogen/cmd/ogen@latest
ogen --target internal/flow/gen --package gen --clean internal/flow/openapi.yaml

The preprocessor converts OpenAPI 3.1 nullable union syntax to the 3.0.3 form ogen accepts. No other semantic changes.

These docs are built with Astro Starlight and live under docs/. To preview locally:

Terminal window
cd docs
npm install
npm run dev

Content is organised by the Diátaxis framework — put new pages under src/content/docs/{tutorials,guides,reference,explanation}/ depending on whether they teach, solve a task, describe, or explain. Cross-page links are relative (../../<group>/<page>/) so they stay correct under the site’s base path.

  • Follow standard Go formatting (gofmt).
  • Keep cyclomatic complexity below 15 per function (gocyclo will catch violations).
  • End comments with a period (godot will catch violations).
  • Use log/slog for all logging — no fmt.Println or log.Printf.
  • Context keys must use unexported struct types to prevent cross-package collisions.