kingtime
Kingtime
Open-source time tracking for freelancers and small studios. Log hours per client and project, import your history from Harvest, prepare invoices as Moneybird drafts with a clean hour specification, and let your LLM do the bookkeeping through the built-in MCP server.
Built with Laravel 13, Inertia v3, Vue 3, shadcn-vue and Tailwind v4. Live at kingtime.nl.
Features
Timesheet with a Monday to Sunday week strip, per-day totals, a running timer that is visible on every page, and a filterable table of every entry ever logged.
Clients and projects with hourly rates, budgets and colours, the way Harvest models them. Archive a project (or a client with all its projects) when the work is done: it disappears from the lists and selects, its hours stay.
Harvest import: one command (or a button on the integrations page) pulls users, clients, projects and all time entries (an entry without notes keeps its Harvest task name as notes). Runs are idempotent and incremental, and can be scheduled hourly.
Invoicing through Moneybird: pick a client and a period, review the unbilled hours grouped per project, and create a draft sales invoice in Moneybird with the full specification attached as a note. Statuses sync back daily.
MCP server: connect Claude Desktop, Claude Code, Cursor or any MCP client with a personal API token and say "log two hours of development on the Acme website for today" or "prepare the September invoice for Globex".
Reports with hours, billable hours, earned and invoiced amounts per week, month or year, with charts and a breakdown per client and project.
Dashboard with hours today, this week and this month against the previous period, a weekly hours chart, billable ratio and top projects.
Passkeys and two-factor authentication out of the box (Laravel Fortify). Sign-up closes automatically after the first user.
Requirements
PHP 8.4 or newer, Composer
Node 22 or newer, npm
SQLite (default) or MySQL/MariaDB/PostgreSQL
Installation
git clone https://github.com/sietzekeuning/kingtime.git
cd kingtime
composer run setup # composer install, .env, key, migrations, npm install, npm run build
composer run dev # php artisan dev: server, queue, logs and Vite in one terminalOpen the app, register the first account (registration closes afterwards), and you are in. Want demo data to look at first? Run php artisan db:seed for a demo user (demo@kingtime.test / password) with six months of entries.
Harvest import
Every user connects their own Harvest account under Settings › Integrations:
Create a personal access token at https://id.getharvest.com/developers and paste it together with the account id shown next to it. Kingtime checks the token against the API before storing it (encrypted, never shown again).
The Harvest user behind the token becomes you: their entries land on your Kingtime account, whatever email either side uses. Other Harvest users are matched by email and created as inactive users when unknown.
Click "Run import now", or use the command line:
php artisan harvest:import --full # everything, the first time php artisan harvest:import # only what changed, for every connected user php artisan harvest:import --user=you@example.com --since=2026-01-01
Every row keeps its harvest_id, so re-running never duplicates anything and local edits to fields Harvest does not know about (project colours, Moneybird contact ids, notes) survive. The scheduler runs the incremental import every hour for every connected user; Settings › Integrations shows your last runs. Disconnecting forgets the token and keeps everything that was imported.
Moneybird invoicing
Every user connects their own Moneybird administration under Settings › Integrations:
Create an API token at https://moneybird.com/user/applications and paste it with the administration id from the Moneybird URL. Optionally add a tax rate id, ledger account id and workflow id; they are applied to every invoice line and invoice you push. Kingtime checks that the token can see the administration before storing it (encrypted, never shown again).
Go to Invoices › Prepare invoice, pick a client and a period, uncheck any entries you want to leave out, and create the draft. With "Push to Moneybird" on, the contact is looked up (or created) by name and a draft sales invoice is created with one line per project and rate and the specification (hours and notes per day) as a note. Nothing is ever sent to your customer from Kingtime; you review and send in Moneybird.
An invoice belongs to the user who prepared it and is pushed and synced with that user's connection. Invoiced entries are locked. Deleting a draft that was not pushed unlocks them again. php artisan invoices:sync-statuses (scheduled daily) pulls the paid/late/open state back from Moneybird for every invoice whose owner is connected.
Connect your LLM (MCP)
Kingtime ships an MCP server at /mcp, authenticated with a personal API token (Settings › API tokens).
claude mcp add --transport http kingtime https://kingtime.nl/mcp \
--header "Authorization: Bearer <your-token>"Claude Desktop, Cursor and friends take the same thing as JSON:
{
"mcpServers": {
"kingtime": {
"url": "https://kingtime.nl/mcp",
"headers": { "Authorization": "Bearer <your-token>" }
}
}
}For a local install without HTTPS you can also run it over stdio: php artisan mcp:start kingtime (acts as the first user in the database).
Tools: list_clients, list_projects, list_time_entries, get_timesheet, log_time, update_time_entry, delete_time_entry, start_timer, stop_timer, get_running_timer, get_unbilled_summary, preview_invoice, prepare_invoice, moneybird_status, moneybird_list_contacts, moneybird_list_sales_invoices, moneybird_get_sales_invoice, moneybird_list_purchase_invoices, moneybird_list_receipts, moneybird_revenue_summary and moneybird_get. Invoices prepared through MCP are drafts, exactly like the ones from the UI. The moneybird_* tools are read-only queries against your Moneybird administration (contacts, sales and purchase invoices, receipts, revenue per month and per contact, and a generic GET for any other endpoint); they never create or change anything. All tools use the connections of the user behind the API token.
Development
composer run dev # app + queue + logs + Vite
./vendor/bin/pest # tests (SQLite in memory, no build needed)
vendor/bin/pint --dirty # PHP formatting
vendor/bin/phpstan analyse --memory-limit=2G
npx vp check --fix && npx vue-tsc --noEmit
php artisan typescript:transform # regenerate resources/js/types/generated.d.ts + lib/enums.ts
php artisan wayfinder:generate --with-formHow the code is organised:
app/Domain/{Client,Project,Time,Invoice,Harvest,Moneybird,Mcp,Dashboard,User,Shared}: models, DTOs (Spatie Laravel Data), tables, actions, controllers per domain.DTOs are the source of truth for the frontend types:
php artisan typescript:transformwritesresources/js/types/generated.d.tsandresources/js/lib/enums.ts, so Vue pages are fully typed end to end.List pages are one
Tableclass (sorting, filtering) plus theDataTablecomponent.CLAUDE.mddocuments the conventions for contributors and coding agents.
Backups
spatie/laravel-backup dumps the database and storage/app every night at 02:00 to the s3 disk (cleanup at 01:30, health check at 03:00), keeping 7 daily, 16 daily-after-that, 8 weekly and 4 monthly archives. Point it at a bucket with a key that can only reach that bucket:
BACKUP_DISK=s3
BACKUP_NOTIFICATION_EMAIL=you@example.com
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=eu-central-1
AWS_BUCKET=your-backup-bucketBACKUP_DISK=local keeps the archives in storage/app/private instead. Run one by hand with php artisan backup:run.
Deploying
The project deploys with Laravel Forge using zero-downtime deployments: GitHub Actions builds the assets and runs the test suite, then force-pushes a deploy branch that Forge picks up. See .github/workflows/production.yml. Any host that runs Laravel works; build the assets with npm run build and run the scheduler (php artisan schedule:run every minute) plus a queue worker for the Harvest import job.
License
MIT. See LICENSE.