A local stdio MCP server that exposes Float time tracking — reviewing and logging time — as Model Context Protocol tools, so you can log and review your hours from Claude instead of the Float GUI.
It is a sibling of the Journal MCP: a single static Go binary, spf13/cobra
CLI, the modelcontextprotocol/go-sdk, served over stdio. The Float API token
is held entirely in the server config — it never enters the model's context,
is never hardcoded, and is never logged or returned in tool output.
- Reviews time — list projects (with billable status), tasks, and a grouped summary of your logged time over any date range.
- Logs time — resolves a project by name (
"Acme","Globex"), runs a duplicate check, and writes only after you confirm. - Edits time — update or delete entries, always preview-first.
- Billable awareness — every project carries its billable (client T&M) vs. non-billable (internal) flag, and warns when Float hasn't set one.
In Float, go to Team Settings → Integrations and create an API token (requires account-owner permission).
The token is read from, in order:
-
the
FLOAT_API_TOKENenvironment variable (e.g. in the MCP connector'senvblock), or -
~/.config/float/config.json:{ "api_token": "YOUR_FLOAT_API_TOKEN", "user_email": "you@example.com" }
user_email is your Float account email (optional). Setting it lets the server
resolve your people_id and display your name without an explicit
float_whoami call — it's looked up live from Float, so there's no separate
"name" to configure. Email resolution order is FLOAT_USER_EMAIL env →
user_email in config.json.
If no token is present, every tool returns a setup message pointing back here.
The config directory also holds a gitignored cache.json (your resolved
people_id and project/task id mappings); it is written by the server and never
contains anything secret beyond ids.
Override the config directory with
FLOAT_CONFIG_DIR(defaults to~/.config/float, honoringXDG_CONFIG_HOME).
Does Claude Desktop use the same config? Yes — the server resolves
~/.config/float/config.json (and cache.json) by absolute path, so the
process Claude Desktop spawns reads the same files as your terminal doctor
runs. Putting your token and user_email in config.json is therefore the
one-place setup that covers both. (Shell env vars like FLOAT_USER_EMAIL do
not propagate into Claude Desktop — for the env route, set them in the
connector's env block instead.) Verify any time with float-mcp doctor.
Homebrew (macOS/Linux):
brew install ericmann/tap/float-mcpInstall script (downloads the latest release, verifies its SHA-256):
curl -fsSL https://raw.githubusercontent.com/ericmann/float-mcp/main/install.sh | sh(FLOAT_MCP_VERSION=v0.1.0 pins a tag; PREFIX=$HOME/.local changes the
location.) Linux users can also grab a .deb/.rpm/.apk from the
releases page.
From source:
make build # -> ./float-mcp (static binary, CGO disabled)
make install # builds + installs to /usr/local/bin (override PREFIX=…)| Tool | What it does |
|---|---|
float_whoami |
Resolve and cache your people_id (looked up by email). Use first to validate the token. |
float_list_projects |
Active projects (filterable), each with its billable flag. billable: null means Float has no flag set — confirm client vs. internal. |
float_list_tasks |
The tasks on a project, so you can pick the task_meta_id / task_name. |
float_list_time |
Your logged time over a date range (optionally one project), grouped by day and/or project with totals. |
Every write tool is two-step. The first call (confirm: false, the default)
returns a preview and writes nothing; you confirm the details, then call
again with confirm: true.
| Tool | What it does |
|---|---|
float_log_time |
Resolve project + task, surface billable status, duplicate-check the day, then (on confirm) log. An identical entry is refused even with confirm: true; a different entry that day returns a conflict so you can choose update vs. add. |
float_update_time |
Preview a before/after, then apply. |
float_delete_time |
Fetch and show the full entry, then delete only on explicit confirm: true. |
- Duplicate check before any log — never double-posts an identical entry.
- Confirm-before-write — previews echo project, billable/internal, date, hours, and notes; nothing is written on the first call.
- Delete shows the full entry and requires explicit confirmation.
- Billable awareness — surfaced on every project, with a warning when ambiguous.
- Resources:
float://me,float://projects,float://timesheet/this-week. - Prompts:
weekly-timesheet(review this week for gaps and mis-bucketed billable/internal work) andlog-day(draft entries from a free-text summary).
Add to claude_desktop_config.json (point command at the built binary and put
the token in env so it stays out of the model's context):
Restart the app and the float_* tools appear.
claude mcp add float \
--env FLOAT_API_TOKEN=YOUR_FLOAT_API_TOKEN \
--env FLOAT_USER_EMAIL=you@example.com \
-- /usr/local/bin/float-mcp mcp(Or omit the --env FLOAT_API_TOKEN and use ~/.config/float/config.json.)
make test # go test ./...
make vet # go vet ./...
make fmt # gofmt -w .
make lint # gofmt check + vet (+ golangci-lint if installed)Tests never touch the network — the Float HTTP boundary is faked with
httptest. The client sets the required headers
(Authorization, User-Agent, Content-Type on writes), respects Float's
200 req/min limit (backs off and retries once on HTTP 429), and maps
403 / 404 / 422 / 429 to actionable errors that never include the token.
Distribution mirrors the Journal MCP: GoReleaser
builds static binaries, archives + cosign-signed checksums, .deb/.rpm/.apk
packages, and the Homebrew cask, on every vX.Y.Z tag.
Cut a release in one click: run the Prepare Release workflow
(Actions → Prepare Release → Run workflow) with a version like v0.1.0. It
finalizes the ## [Unreleased] section of CHANGELOG.md to that version + the
date, commits it, pushes an annotated tag, and dispatches Release, which
runs GoReleaser — using your curated CHANGELOG section as the GitHub Release
notes and pushing the updated cask to ericmann/homebrew-tap.
So the only manual step each cycle is keeping ## [Unreleased] in CHANGELOG.md
current as you merge work.
One-time setup in this repo's settings:
- Add a secret
HOMEBREW_TAP_TOKEN— a GitHub PAT with write access toericmann/homebrew-tap(so the release can push the cask). The defaultGITHUB_TOKENalready covers creating the release on this repo, and cosign signs keylessly via GitHub OIDC (no key to manage).
Validate the config locally without publishing:
make snapshot # builds dist/ + the cask, no publish, no signing
{ "mcpServers": { "float": { "command": "/usr/local/bin/float-mcp", "args": ["mcp"], "env": { "FLOAT_API_TOKEN": "YOUR_FLOAT_API_TOKEN", "FLOAT_USER_EMAIL": "you@example.com" } } } }