A Model Context Protocol server for Clockify — plug any MCP client into your time-tracking workspace and let it log time, run reports, and manage projects on your behalf.
Unofficial community project. Not affiliated with or endorsed by Clockify. Single-maintainer, public source under MIT, see SUPPORT.md and GOVERNANCE.md for adoption posture.
Works with Claude Code, Claude Desktop, Cursor, Codex, and anything else that speaks MCP.
- 163 tools — 53 always-on (timer, entries, projects, clients, tags, tasks, reports, agent workflows, tool activation, …) plus 110 on-demand (invoices, scheduling, approvals, admin, documented API helpers, …) across 12 activatable groups.
- Resources & prompts — six
clockify://URI templates and ten built-in prompt templates alongside the tool surface. - Five policy modes —
read_only,time_tracking_safe,safe_core,standard,full— plus dry-run previews on every write tool and HMAC confirmation tokens for high-risk tool calls (ADR 0018). - Three transports — stdio (default), streamable HTTP 2025-03-26 (shared services), opt-in gRPC behind a build tag. Cancellation,
tools/list_changed, size limits, and malformed-JSON boundaries pinned with cross-transport parity tests. - Stdlib-only default build — zero external runtime dependencies; the default binary links no OpenTelemetry, gRPC, or protobuf symbols (verified in CI).
- Signed releases — every binary and container image ships with cosign signatures and SPDX SBOMs; SLSA build provenance is attached when GitHub artifact attestations are available for the repository account tier.
- Start Here · Install · Connect a client · Common workflows · Tool tiers
- Policy modes · Configuration · Architecture · Docker · Build and test
- Compatibility · Troubleshooting · Deployment · Contributing
Pick a deployment profile and invoke the binary with --profile=<name> (or MCP_PROFILE=<name>). The profile applies a bundle of pinned defaults; explicit env overrides still win.
| Profile | Shape | Doc | Example env |
|---|---|---|---|
local-stdio |
single user, stdio subprocess | profile-local-stdio.md | env.local-stdio.example |
single-tenant-http |
one team, streamable HTTP + static bearer | profile-single-tenant-http.md | env.single-tenant-http.example |
shared-service |
multi-tenant, postgres + OIDC, audit fail-closed | production-profile-shared-service.md | env.shared-service.example |
private-network-grpc |
gRPC + mTLS behind a private perimeter (-tags=grpc) |
profile-private-network-grpc.md | env.private-network-grpc.example |
prod-postgres |
alias of shared-service with ENVIRONMENT=prod |
see shared-service doc | — |
Not sure which profile matches your environment? Run clockify-mcp doctor — it prints every env var's effective value, its source (explicit / profile / default / empty), and whether Load() would succeed at startup. Add --strict for hosted-service posture checks. Exit code is 0 on a clean load, 2 on a load error, and 3 when strict posture findings are present.
Operators upgrading from before Wave I: see the operator overview and docs/operators/ for the deeper production checklist — profiles shortcut the common cases but do not replace ops review for critical deployments.
# Go
go install github.com/apet97/go-clockify/cmd/clockify-mcp@latest
# npm (prebuilt binaries)
npx @apet97/clockify-mcp-go
# Or download a prebuilt binary from Releases:
# https://github.com/apet97/go-clockify/releasesVerify:
clockify-mcp --versionv1.2.5 is the current stable community/self-hosted AIII-backed
API-refresh line, released on 2026-05-13
(GitHub Release ·
46 signed assets · npm @apet97/clockify-mcp-go dist-tags.latest=1.2.5
· container image ghcr.io/apet97/go-clockify:1.2.5). It carries the
refreshed 163-tool Clockify API catalog and the generated 192-operation
OpenAPI artifact, including AIII-sourced entity-change endpoints, the
expense detailed report route, live invoice/member-profile/webhook
contract corrections, and the quarantined /scheduling/capacity
phantom route.
Operators verifying signatures should follow
docs/verification.md. The original
community/self-hosted launch evidence remains anchored to v1.2.1;
paid-hosted / commercial / "official Clockify" framing is explicitly out
of scope per
docs/launch-readiness-review-may-8.md
§ "Deferred paid-hosted/commercial follow-ups …".
If your environment routes Go module fetches through a custom proxy or
VCS resolver and you want to skip the public proxy for this module,
go env -w GOPRIVATE=github.com/apet97/go-clockify is supported but
not required for a standard go install against the public repo.
Get a Clockify API key from Profile → Advanced and export it:
export CLOCKIFY_API_KEY=your-keyThe key inherits its owner's Clockify workspace role (Regular / Team
Manager / Workspace Admin / Owner). The MCP layer's CLOCKIFY_POLICY
controls which tools are exposed; the Clockify role controls whether
those tools succeed at the API. See
docs/policy/production-tool-scope.md
for the minimum role per tool family.
For personal testing against one real workspace, pin the local stdio profile and workspace before connecting an AI client:
export MCP_PROFILE=local-stdio
export CLOCKIFY_WORKSPACE_ID=your-workspace-id
export CLOCKIFY_POLICY=time_tracking_safeUse time_tracking_safe for exploratory AI-facing testing because it
allows timer and time-entry workflows without project/client/tag/task
creation. Switch to safe_core only when you intentionally want that
workspace-shaping surface. For large workspaces, keep
CLOCKIFY_REPORT_MAX_ENTRIES at its fail-closed default unless you have
a specific reason to materialize more than 10,000 raw entries in one
tool call.
The examples below are the local stdio path: your MCP client launches clockify-mcp as a subprocess and forwards CLOCKIFY_API_KEY in its environment. Copy-ready snippets also live in examples/.
claude mcp add clockify -- clockify-mcpThen set the personal tester env vars above in your shell before
starting Claude Code. If you inline env with claude mcp add -e,
include MCP_PROFILE, CLOCKIFY_API_KEY, CLOCKIFY_WORKSPACE_ID, and
CLOCKIFY_POLICY.
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"clockify": {
"command": "clockify-mcp",
"env": {
"MCP_PROFILE": "local-stdio",
"CLOCKIFY_API_KEY": "your-key",
"CLOCKIFY_WORKSPACE_ID": "your-workspace-id",
"CLOCKIFY_POLICY": "time_tracking_safe"
}
}
}
}Add to .cursor/mcp.json in your workspace:
{
"mcpServers": {
"clockify": {
"command": "clockify-mcp",
"env": {
"MCP_PROFILE": "local-stdio",
"CLOCKIFY_API_KEY": "your-key",
"CLOCKIFY_WORKSPACE_ID": "your-workspace-id",
"CLOCKIFY_POLICY": "time_tracking_safe"
}
}
}
}Add to your Codex MCP config:
{
"mcpServers": {
"clockify": {
"command": "clockify-mcp",
"env": {
"MCP_PROFILE": "local-stdio",
"CLOCKIFY_API_KEY": "your-key",
"CLOCKIFY_WORKSPACE_ID": "your-workspace-id",
"CLOCKIFY_POLICY": "time_tracking_safe"
}
}
}
}If you installed via npm/npx, swap the command for:
{
"command": "npx",
"args": ["@apet97/clockify-mcp-go"]
}For longer intent-keyed examples, see the agent cookbook.
Built-in prompt templates are available through MCP prompts/list and
prompts/get: log-week-from-calendar, weekly-review,
find-unbilled-hours, find-duplicate-entries, and
generate-timesheet-report, plus recipes for invoices, approvals,
time off, scheduling, and webhook rollout. They are short planning
prompts that point clients at the current structured tools, including
clockify_log_time for finished past entries and
clockify_timesheet_review for gaps, overlaps, and suggested
next-tool actions.
Start and stop a timer:
→ clockify_start_timer { "project": "My Project" }
← { "ok": true, "action": "timer_started", "data": { "id": "abc123" } }
→ clockify_stop_timer {}
← { "ok": true, "action": "timer_stopped" }
Log time retroactively:
→ clockify_log_time { "project": "Project Alpha", "start": "today 9:00", "end": "today 11:00", "description": "Code review", "dry_run": true }
Review and close a timesheet gap:
→ clockify_timesheet_review { "date": "2026-05-03", "timezone": "UTC" }
← { "ok": true, "action": "clockify_timesheet_review", "data": { "issues": [], "suggestedActions": [] } }
→ clockify_timesheet_fill_gap { "project": "Project Alpha", "start": "2026-05-03T09:00:00Z", "end": "2026-05-03T10:00:00Z", "description": "Planning", "dry_run": true }
Discover a Tier 2 domain or tool:
→ clockify_list_tools { "query": "invoice" }
← { "count": 6, "all_results": [ { "type": "group", "name": "invoices" }, { "type": "tool", "name": "clockify_send_invoice" } ] }
Activate a Tier 2 domain:
→ clockify_activate_group { "name": "invoices" }
← { "activated": "invoices", "tool_count": 12, "activated_tools": ["clockify_list_invoices", "..."], "total_visible_tools": 47 }
Optionally activate a single Tier 2 tool:
→ clockify_activate_tool { "name": "clockify_send_invoice" }
← { "activated": "clockify_send_invoice", "group": "invoices", "tool_count": 12, "activated_tools": ["clockify_list_invoices", "..."], "total_visible_tools": 47 }
Deactivate the domain when the task is done:
→ clockify_deactivate_group { "name": "invoices" }
← { "deactivated": "invoices", "tool_count": 12, "deactivated_tools": ["clockify_list_invoices", "..."], "total_visible_tools": 35 }
Preview a high-risk operation first to mint a confirmation token (ADR 0018):
→ clockify_delete_entry { "entry_id": "abc123", "dry_run": true }
← {
"dry_run": true,
"preview": { "id": "abc123", "description": "Meeting" },
"note": "No changes were made.",
"confirmation_required": true,
"confirmation_token": "eyJ...example",
"confirmation_expires_at": "2026-05-11T19:05:00Z",
"confirmation_risk_class": ["destructive"],
"confirmation_note": "Re-submit the same arguments with dry_run:false and confirmation_token to execute."
}
The dry-run envelope shape is "rich preview for destructive tools that
have a read counterpart, minimal envelope otherwise" — high-risk
non-destructive tools (e.g. clockify_send_invoice,
clockify_test_webhook) return only the confirmation fields plus an
args echo. The token binds to the tool name, the argument
fingerprint, the principal (tenant + subject + session), and an
exp; changing any of those between mint and execute invalidates it.
Execute with the same arguments plus the minted token:
→ clockify_delete_entry { "entry_id": "abc123", "dry_run": false, "confirmation_token": "eyJ...example" }
← { "ok": true, "action": "clockify_delete_entry", "data": { "deleted": true, "entryId": "abc123" } }
Ordinary RiskWrite tools (the time-entry / timer surface — e.g.
clockify_log_time, clockify_start_timer) do not require a
confirmation token. Pass dry_run:false or omit it to execute
directly; dry_run:true still returns a preview envelope.
Large workspace report hygiene:
- Pin
CLOCKIFY_WORKSPACE_IDfor personal API-key testing even when auto-detection works today. It keeps the target stable if the key later gains access to another workspace. - Prefer short date ranges and
include_entries=falsefor summary, weekly, detailed, and quick reports when you need totals rather than raw rows. - Use
pageandpage_sizeon list tools. The default ispage=1,page_size=50, and the maximum page size is 200. - Report responses expose
meta.paginationandmeta.limits.CLOCKIFY_REPORT_MAX_ENTRIESapplies when a call materializes raw entries withinclude_entries=true; cap errors are intentional fail-closed behavior.
Tier 1 (58 tools, always loaded): timer, entries, projects (incl. create/update/delete/archive), clients (full CRUD plus client report), tags (full CRUD), tasks (full CRUD), users, workspaces, governance, reports, read-only business briefs, workflows, search, activation, context.
Tier 2 (119 tools, 13 groups, on demand): invoices, expenses, scheduling, time off, approvals, shared reports, user admin, webhooks, custom fields, groups/holidays, project admin, change tracking, and probe_lab_api documented-API escape hatch tools.
Call clockify_list_tools to discover a Tier 2 group or specific tool, clockify_activate_group / clockify_activate_tool to widen the current session, and clockify_deactivate_group to shrink the visible surface after a task. Activation updates tools/list at runtime, and activated_tools only lists names that are visible after bootstrap and policy filtering. clockify_search_tools remains as a deprecated compatibility shim for older clients.
CLOCKIFY_POLICY controls which tools are exposed based on trust level:
| Mode | Read | Write | Delete | Tier 2 | Use case |
|---|---|---|---|---|---|
read_only |
yes | no | no | no | Untrusted agents — observe only |
time_tracking_safe |
yes | time-entry allowlist | no | no | Recommended AI-facing default for time tracking |
safe_core |
yes | broader allowlist | no | no | Trusted assistants that may create projects, clients, tags, and tasks |
standard |
yes | yes | yes | on demand | Raw no-profile default / trusted operator mode; code-level allow rules match full |
full |
yes | yes | yes | on demand | Admin automation label for operators intentionally running the unrestricted policy |
Introspection tools (clockify_whoami, clockify_policy_info, clockify_list_tools, clockify_activate_group, clockify_activate_tool, clockify_deactivate_group, clockify_search_tools, clockify_resolve_name, clockify_resolve_debug) are always available regardless of policy. clockify_resolve_debug is a compatibility alias; new clients should call clockify_resolve_name.
standard and full share broad policy allow rules: any non-denied
tool or group is policy-allowed. Their operational difference is
convention plus bootstrap/activation posture. A separate ADR 0018
confirmation-token gate sits alongside the policy mode — when
CLOCKIFY_CONFIRMATION_TOKENS=enabled (the default), every
high-risk tool call (billing, admin, permission-change,
external-side-effect, or destructive) requires a server-minted HMAC
token obtained via a dry_run:true preview, regardless of policy
mode. time_tracking_safe and safe_core remain the recommended
AI-facing modes for hosted deployments, and CLOCKIFY_DENY_*,
CLOCKIFY_ALLOW_GROUPS, and CLOCKIFY_BOOTSTRAP_MODE narrow a
trusted operator deployment further.
The essentials (regenerate with go run ./cmd/gen-config-docs -mode=all):
| Variable | Default | Purpose |
|---|---|---|
CLOCKIFY_API_KEY |
— |
API key (required for stdio/http/grpc and MCP_PROFILE=single-tenant-http; optional for shared-service / prod-postgres streamable_http where tenant credentials come from the control plane) |
CLOCKIFY_BOOTSTRAP_MODE |
full_tier1 |
Initial tool surface |
CLOCKIFY_CONFIRMATION_TOKENS |
enabled |
Require an HMAC confirmation token (minted on dry_run:true) for high-risk tool calls per docs/adr/0018-risk-class-confirmation-tokens.md; high-risk covers RiskBilling, RiskAdmin, RiskPermissionChange, RiskExternalSideEffect, and RiskDestructive. Set to disabled only for break-glass single-operator deployments. |
CLOCKIFY_DEDUPE_MODE |
warn |
Duplicate entry detection |
CLOCKIFY_DRY_RUN |
enabled |
Enable dry-run preview support for tools that expose dry_run:true |
CLOCKIFY_POLICY |
standard |
Tool-access policy tier |
CLOCKIFY_RATE_LIMIT |
120 |
Global tool calls per 60s window (0=disabled). For multi-subject HTTP/gRPC deployments, size this at least active_subjects * CLOCKIFY_PER_TOKEN_RATE_LIMIT so per-subject fairness can engage. |
CLOCKIFY_WORKSPACE_ID |
auto |
Workspace ID (auto-detected if only one) |
MCP_ALLOW_DEV_BACKEND |
— |
Permit memory/file backends for streamable_http or grpc (single-process only) |
MCP_AUDIT_DURABILITY |
best_effort |
Audit persist-failure behaviour (defaults to fail_closed when ENVIRONMENT=prod); fail_closed_strict also surfaces post-mutation outcome persistence failures |
MCP_AUTH_MODE |
— |
Authentication mode (per-transport support varies; see matrix) |
MCP_CONTROL_PLANE_AUDIT_CAP |
0 |
File/memory audit cap (0=unbounded). Postgres uses retention instead. |
MCP_CONTROL_PLANE_AUDIT_RETENTION |
720h |
Audit retention [1h,8760h]; 0=off |
MCP_CONTROL_PLANE_DSN |
memory |
Control-plane DSN: memory, file://, postgres://... |
MCP_GRPC_BIND |
:9090 |
gRPC listen address (requires -tags=grpc) |
MCP_HTTP_BIND |
:8080 |
HTTP listen address |
MCP_HTTP_INLINE_METRICS_AUTH_MODE |
inherit_main_bearer |
Auth mode for inline /metrics |
MCP_HTTP_INLINE_METRICS_ENABLED |
0 |
Expose /metrics on the main HTTP listener |
MCP_HTTP_LEGACY_POLICY |
warn |
Legacy HTTP startup behaviour (defaults to deny when ENVIRONMENT=prod) |
MCP_HTTP_MAX_BODY |
4194304 |
Deprecated — use MCP_MAX_MESSAGE_SIZE. Deprecated alias for MCP_MAX_MESSAGE_SIZE |
MCP_HTTP_RATELIMIT_GET_PER_SESSION |
0 |
Concurrent streamable HTTP SSE GET connections allowed per session on this process (0=disabled; hosted profiles default to 4) |
MCP_HTTP_RATELIMIT_PER_IP |
0 |
Process-local HTTP admission limit per source IP per minute (0=disabled; hosted profiles default to 600) |
MCP_HTTP_RATELIMIT_PER_PRINCIPAL |
0 |
Process-local HTTP admission limit per authenticated subject+tenant per minute (0=disabled; hosted profiles default to 300) |
MCP_LOG_FORMAT |
text |
Log format (stderr; PII-scrubbed) |
MCP_MAX_MESSAGE_SIZE |
4194304 |
Max request size in bytes (primary knob); 0 < N <= 104857600 |
MCP_MAX_SESSIONS_PER_PRINCIPAL |
0 |
Streamable HTTP session cap per authenticated subject+tenant on this replica (0=disabled; initialize requests beyond the cap return 429). Bounds blast radius when a single principal opens many initialize sessions. |
MCP_MAX_SESSIONS_PER_REPLICA |
0 |
Streamable HTTP session cap per replica (0=disabled; initialize requests beyond the cap return 503 with Retry-After). Pair with a per-principal cap to prevent a single tenant from starving the pool. |
MCP_METRICS_AUTH_MODE |
static_bearer (when MCP_METRICS_BIND set) |
Auth mode for dedicated metrics listener |
MCP_METRICS_BEARER_TOKEN |
— |
Bearer token (>=16 chars) for static_bearer metrics |
MCP_METRICS_BIND |
— |
Dedicated metrics listener (optional; recommended for streamable_http) |
MCP_OIDC_VERIFY_CACHE_TTL |
60s |
OIDC verify cache TTL [1s,5m]; hosted profiles clamp values above 60s |
MCP_PROFILE |
— |
Apply a bundle of pinned defaults for a named deployment shape; explicit env overrides still win |
MCP_TENANT_POLICY_CEILING |
— |
Maximum policy mode a control-plane tenant record may select via TenantRecord.PolicyMode. Hosted profiles (shared-service, prod-postgres) default this to time_tracking_safe so a corrupted tenant row cannot broaden the process posture. Empty = no explicit ceiling; the process mode acts as the implicit ceiling. Streamable-HTTP only — the gRPC transport does not consume control-plane tenant records. Ignored on stdio (no tenants). See docs/adr/0021-hosted-tenant-policy-ceiling.md. |
MCP_TRANSPORT |
stdio |
Transport mode; http is legacy POST-only (deprecated) |
Run clockify-mcp --help for the complete list (75+ variables covering concurrency, timeouts, control plane, metrics, auth, CORS, and webhook DNS validation).
Four clean layers: protocol core (internal/mcp/), Clockify client (internal/clockify/), tool surface (internal/tools/), and safety layer (internal/enforcement/). The protocol core has zero domain imports and plugs into the rest via Enforcement, Activator, Notifier, and ResourceProvider interfaces.
The published image defaults to the spec-strict streamable HTTP transport.
The single-tenant-http profile wires the matching auth + control-plane
defaults so the example below boots without a Postgres container or an
OIDC issuer.
docker build -f deploy/Dockerfile -t clockify-mcp .
docker volume create clockify-mcp-data
export MCP_BEARER_TOKEN="$(openssl rand -base64 24)"
docker run -p 8080:8080 \
-v clockify-mcp-data:/var/lib/clockify-mcp \
-e MCP_PROFILE=single-tenant-http \
-e CLOCKIFY_API_KEY=your-key \
-e MCP_BEARER_TOKEN="$MCP_BEARER_TOKEN" \
clockify-mcpValidate the running server with the operator-facing endpoints:
curl http://127.0.0.1:8080/health
curl --oauth2-bearer "$MCP_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
http://127.0.0.1:8080/mcpThe repository also ships deploy/docker-compose.yml with a Caddy reverse proxy for TLS termination, and a Helm chart at deploy/helm/.
make check # fast inner loop: gofmt + go vet + go test
make verify # pre-PR local pipeline: lint, coverage floors, fuzz-short,
# build-tag checks, HTTP smoke, k8s render, govulncheck
# (lint/k8s/fips/vuln tiers skip when local tools are missing)
make release-check # pre-ship/tag gate: docs, scripts, smokes, gRPC E2E, deploy render
make cover # coverage report
make build # binary with version from git tagsmake verify mirrors the PR-blocking CI jobs that can run on a laptop
when their tools are installed; CI remains authoritative for skipped
local tiers. make release-check is the laptop-runnable pre-ship gate.
Neither command replaces the external launch-candidate evidence gates
tracked in docs/launch-candidate-checklist.md.
Go 1.25.10, stdlib only. Module path: github.com/apet97/go-clockify.
| Component | Version |
|---|---|
| MCP Protocol | 2025-11-25 (back-compat: 2025-06-18, 2025-03-26, 2024-11-05) |
| Go | 1.25.10 pinned |
| Node.js (npm wrapper) | 18+ |
No tools visible — Check CLOCKIFY_BOOTSTRAP_MODE. In minimal mode most tools are hidden; use clockify_list_tools to discover them.
401 Unauthorized — API key is invalid or expired. Generate a new one.
Multiple workspaces — Set CLOCKIFY_WORKSPACE_ID explicitly.
Tool not found — It may be a Tier 2 tool. Use clockify_list_tools to find it, then clockify_activate_group or clockify_activate_tool to activate its group.
Dry-run not working — Ensure CLOCKIFY_DRY_RUN=enabled (default) and pass "dry_run": true in tool call parameters.
Stale tool list — Stdio, streamable_http, and grpc clients all receive notifications/tools/list_changed after activation (streamable_http via the SSE stream on GET /mcp; gRPC fans the notification through every active Exchange stream). Only legacy http clients must manually re-fetch tools/list.
Reference Kubernetes manifests live in deploy/k8s/ and deploy/helm/: Deployment (non-root distroless, read-only root FS, dropped capabilities), NetworkPolicy (default-deny), PodDisruptionBudget, ServiceMonitor, and a PrometheusRule with burn-rate alerts for a 99.9% SLO.
For a single-page operator overview that links the threat model, transports, auth modes, deployment targets, runbooks, and compliance posture, see the operator overview.
- Shared Service profile
- Support Matrix
- Client Compatibility
- Deploy-Readiness Checklist
- Operator Guides (Shared Service vs Self-Hosted)
See CONTRIBUTING.md.
Autonomous agents should start with AGENTS.md and the current launch-state handoff in docs/agent-handoff.md. The Claude Code continuation packet is retained as the historical post-PR #51 handoff.
- Adoption expectations, response-time posture,
v1.xwire-format stability guarantee: SUPPORT.md - Governance (single-maintainer, merge gate, sensitive-area self-review): GOVERNANCE.md
- Security vulnerabilities: SECURITY.md (private disclosure channel)
- Bug reports and feature requests: GitHub Issues
- Release history: CHANGELOG.md · GitHub Releases
- Versioning, support window, breaking-change policy: docs/release-policy.md
- Documentation map: docs/README.md
See NOTICE.md — go-clockify is an independent
third-party client and is not affiliated with, endorsed by, or
sponsored by CAKE.com or Clockify. "Clockify" is a trademark of
CAKE.com.