Skip to content

Latest commit

 

History

History
209 lines (155 loc) · 7.84 KB

File metadata and controls

209 lines (155 loc) · 7.84 KB

float-mcp

CI

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.

What it does

  • 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.

Setup

1. Get a Float API token

In Float, go to Team Settings → Integrations and create an API token (requires account-owner permission).

2. Give the token to the server

The token is read from, in order:

  1. the FLOAT_API_TOKEN environment variable (e.g. in the MCP connector's env block), or

  2. ~/.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, honoring XDG_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.

3. Install

Homebrew (macOS/Linux):

brew install ericmann/tap/float-mcp

Install 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=…)

Tools

Read (no confirmation)

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.

Write (preview → confirm)

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.

Safeguards

  1. Duplicate check before any log — never double-posts an identical entry.
  2. Confirm-before-write — previews echo project, billable/internal, date, hours, and notes; nothing is written on the first call.
  3. Delete shows the full entry and requires explicit confirmation.
  4. Billable awareness — surfaced on every project, with a warning when ambiguous.

Resources & prompts

  • Resources: float://me, float://projects, float://timesheet/this-week.
  • Prompts: weekly-timesheet (review this week for gaps and mis-bucketed billable/internal work) and log-day (draft entries from a free-text summary).

Register as a connector

Claude Desktop

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):

{
  "mcpServers": {
    "float": {
      "command": "/usr/local/bin/float-mcp",
      "args": ["mcp"],
      "env": {
        "FLOAT_API_TOKEN": "YOUR_FLOAT_API_TOKEN",
        "FLOAT_USER_EMAIL": "you@example.com"
      }
    }
  }
}

Restart the app and the float_* tools appear.

Claude Code

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.)

Development

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.

Releasing

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 to ericmann/homebrew-tap (so the release can push the cask). The default GITHUB_TOKEN already 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