bill4time-mcp
This MCP server exposes read-only Bill4Time legal billing and time-tracking data to Claude Desktop or other MCP-compatible clients through a set of query tools.
List and retrieve clients, including active and disabled clients, with OData filter_expr and top/orderby options.
List and retrieve projects, including projects for a client, closed projects, and projects by billing method (Hourly, Flat Fee, Percentage).
List and retrieve time entries by client, project, user, invoice, date range (YYYY-MM-DD), or billing status (Ready For Summary, Ready For Billing, Billing Complete, Pending Project Close).
List and retrieve expenses by client, project, invoice, or date range.
List and retrieve invoices by client, project, prebill/finalized/partially paid/paid status, or date range.
List and retrieve payments by client, project, or date range.
List and retrieve payments-applied records by invoice, payment, or date range.
List and look up users, contacts (including status/date-range/contact-connection filters), and trust accounting records by client, project, or date range.
Use OData filter expressions and pagination/order controls on many list endpoints, and get individual records by ID where provided.
All current tools are read-only; the Bill4Time API does not support create/update/delete operations through this server.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@bill4time-mcp Show all unpaid invoices"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Bill4Time MCP server
Give Claude and other MCP clients read-only access to Bill4Time billing, time and trust data.
Bill4Time MCP server is a Model Context Protocol server for Bill4Time, the time tracking and billing platform. It registers 59 tools, all read-only. It runs over stdio by default, for desktop clients such as Claude Desktop, and offers an opt-in stateless Streamable HTTP mode that implements MCP specification 2026-07-28. Bill4Time credentials stay on the machine that runs the server: they come from the setup command and your operating system's keyring, never from the client. The Bill4Time API is currently read-only.
Features
Clients: list clients, filter by status, and look up active or disabled clients.
Projects: list projects, and filter by client, status or billing method.
Time entries: list by client, project, user, invoice, date range or billing status.
Expenses: list by client, project, invoice or date range.
Invoices: list by status (prebill, finalized, paid, partially paid, unpaid), client, project or date range.
Payments: list payments by client, project or date range, and see which invoices each payment was applied to.
Contacts and users: list contacts by status or date range, list contact connections, and look up users.
Trust accounting: list trust records by client, project or date range.
Filtering: pass OData-style
filter_exprexpressions to the general list tools for advanced queries.
Related MCP server: Clio Manage MCP
Tools
The server registers 59 tools, all of them read-only.
get_clientget_contactget_expenseget_invoiceget_paymentget_payment_appliedget_projectget_time_entryget_trust_recordget_userlist_active_clientslist_active_contactslist_clientslist_closed_projectslist_contact_connectionslist_contact_connections_for_clientlist_contact_connections_for_contactlist_contact_connections_for_projectlist_contactslist_contacts_for_date_rangelist_disabled_clientslist_expenseslist_expenses_for_clientlist_expenses_for_date_rangelist_expenses_for_invoicelist_expenses_for_projectlist_finalized_invoiceslist_invoiceslist_invoices_for_clientlist_invoices_for_date_rangelist_invoices_for_projectlist_open_projectslist_paid_invoiceslist_partially_paid_invoiceslist_paymentslist_payments_appliedlist_payments_applied_for_date_rangelist_payments_applied_for_invoicelist_payments_applied_for_paymentlist_payments_for_clientlist_payments_for_date_rangelist_payments_for_projectlist_prebill_invoiceslist_projectslist_projects_by_billing_methodlist_projects_for_clientlist_time_entrieslist_time_entries_by_billing_statuslist_time_entries_for_clientlist_time_entries_for_date_rangelist_time_entries_for_invoicelist_time_entries_for_projectlist_time_entries_for_userlist_trust_recordslist_trust_records_for_clientlist_trust_records_for_date_rangelist_trust_records_for_projectlist_unpaid_invoiceslist_users
Prompts and resources
The server also registers three prompts and three resources.
Prompt | What it does |
| Builds the full billing picture for one client: projects, time, invoices and payments. Takes |
| Reviews a client's trust accounting records for compliance. Takes |
| Reports unbilled time entries for a date range. Takes |
Resource | What it provides |
| Active clients. |
| Bill4Time users (the timekeeper list). |
| Security notes for the server, including the API key in the URL path. |
Requirements
Python 3.10 or later.
A Bill4Time account with an API key (create one in Settings → API tab).
An MCP client such as Claude Desktop.
Installation
Install uv, then clone the repository and install its locked dependencies:
git clone https://github.com/RosenAdvertising/bill4time-mcp.git
cd bill4time-mcp
uv sync --lockedReleases are also published to PyPI: pip install bill4time-mcp installs version 0.3.0, which predates the HTTP mode described below. Install from source to use HTTP mode.
Configuration
Run the setup command once. It prompts for your API key, tests the connection, and saves the key to your operating system's keyring (see Credential storage):
uv run bill4time-mcp-setupAfter changing the API key, restart the MCP server or client session so the new key is loaded.
Check the connection:
uv run bill4time-mcp-verifyServer messages that say to run bill4time-mcp-setup mean uv run bill4time-mcp-setup from your clone.
The server reads these variables, which you can also set in its environment:
Variable | Required | Default | Purpose |
| Yes (saved by setup) | Credential store | Bill4Time API key. |
| No |
| Set to |
Credential storage
By default the API key is stored in your operating system's native secret store
via the cross-platform keyring library:
OS | Backend |
macOS | Keychain |
Windows | Credential Manager |
Linux | Secret Service (GNOME Keyring / KWallet) |
The secret is saved under the service name bill4time-mcp. Nothing is written to
disk in clear text.
File fallback. On a host with no keyring backend (e.g. a headless Linux box
without Secret Service), or if you set BILL4TIME_MCP_USE_KEYRING=0, the key
falls back to a ~/.bill4time-mcp/.env file with 0600 permissions.
On Windows, the file is stored in the user's profile and protected by Windows'
default per-user access rules. On POSIX, files are created with 0600 permissions
and writes fail closed if private permissions cannot be established.
Read order. A key already set in the process environment wins. Otherwise the server reads the OS keyring, then the .env file fallback. To make a key rotated in the keyring take effect, unset BILL4TIME_API_KEY in the shell that starts the server.
Pluggable backend. keyring lets you point at any secret store. For example,
install keyrings.cryptfile for
an encrypted file backend, or a cloud backend, then select it with the standard
PYTHON_KEYRING_BACKEND environment variable or a keyringrc.cfg. See the
keyring configuration docs.
Usage with Claude Desktop
Add the server to Claude Desktop's configuration file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):
{
"mcpServers": {
"bill4time": {
"command": "uv",
"args": ["run", "--locked", "--directory", "/absolute/path/to/bill4time-mcp", "bill4time-mcp"]
}
}
}Replace /absolute/path/to/bill4time-mcp with the path of your clone, then restart Claude Desktop. Any other stdio MCP client uses the same command and arguments.
HTTP mode
Stdio is the default. Set BILL4TIME_MCP_TRANSPORT=streamable-http to serve the stateless Streamable HTTP transport from MCP specification 2026-07-28 at /mcp. Each request stands alone: no initialization handshake and no Mcp-Session-Id. Clients on earlier protocol versions are served on the same endpoint.
Security: this endpoint has no authentication and no TLS. Anyone who can reach the port can run every tool with this server's vendor credentials. Keep the default loopback bind (
127.0.0.1), or put the server behind an authenticating TLS proxy on a private network.BILL4TIME_MCP_ALLOWED_HOSTSandBILL4TIME_MCP_ALLOWED_ORIGINSprotect against browser DNS rebinding, not against direct callers. A proxy in front of it needs connection and idle timeouts: a legacy-styleGET /mcpwithAccept: text/event-streamholds a stream open until the client disconnects.
Variable | Default | Purpose |
|
|
|
|
| Bind address. |
|
| Port; must be an integer. |
| unset | Comma-separated |
| unset | Comma-separated |
Bill4Time credentials come from the same configuration as stdio (see Configuration), never from the request.
BILL4TIME_MCP_TRANSPORT=streamable-http PORT=8080 uv run --locked bill4time-mcpPoint the MCP client at http://127.0.0.1:8080/mcp.
Error handling
A failed tool call returns an MCP error result (isError). Expected failures return a fixed message prefixed with Error executing tool and the tool name, for example "Error executing tool list_clients: Bill4Time resource was not found. Check the requested record." The server never passes a Bill4Time response body, a request URL or the API key back to the client.
Situation | What the tool returns |
API key missing | "Missing BILL4TIME_API_KEY. Set it or run bill4time-mcp-setup. Restart the MCP server after setup." |
HTTP 401 | "Bill4Time authorization was rejected or expired. Reauthorize with bill4time-mcp-setup." |
HTTP 403 | "Bill4Time access denied: the connected account lacks permission for this action (or the authorization expired; re-run bill4time-mcp-setup if so)." |
HTTP 404, or a | "Bill4Time resource was not found. Check the requested record." |
HTTP 429 | "Bill4Time rate limit reached. Retry after N seconds." where N comes from the |
Any other error status, or a redirect | "Bill4Time returned HTTP 500: server error" (the status varies). The reason after the colon comes from a fixed list: invalid API key, authorization rejected, access denied, resource not found, rate limit reached, service temporarily unavailable, server error, or request failed. |
Timeout or connection failure | "Bill4Time read failed because the connection timed out or was unavailable. Check the connection and retry." |
Response that is not JSON | "Bill4Time returned an unreadable response. Try again later." |
Invalid arguments | A message such as "Argument top must be an integer from 1 to 200." or "Argument start_date must use YYYY-MM-DD format." |
Anything else | "Error executing tool list_clients" (the tool name) with no detail. |
Every Bill4Time request has a 30-second timeout. The server does not retry failed requests and does not follow redirects. A failed resource read returns the same fixed messages, "Bill4Time resource was not found." for an unknown resource and "Error reading resource" for an unexpected failure.
At startup the server exits with a message on stderr and a non-zero status when BILL4TIME_MCP_TRANSPORT is neither stdio nor streamable-http, when PORT is not an integer, or when a non-loopback BILL4TIME_MCP_HOST is set without BILL4TIME_MCP_ALLOWED_HOSTS.
Authentication notes
Bill4Time uses an API key embedded directly in the URL path:
https://secure.bill4time.com/b4t-api/{api_key}/v1/{resource}No OAuth or token refresh required. Create API keys from Settings → API in your Bill4Time account.
OData filtering
General collection list tools accept a filter_expr parameter for advanced filtering:
"status eq 'Active'"
"clientId eq 751"
"invoiceDate ge '2024-01-01' AND invoiceDate le '2024-12-31'"
"billingStatus eq 'Ready For Billing'"Supported operators: eq, ne, gt, ge, lt, le
Every list tool defaults to 50 records and an explicit deterministic sort
(id desc, except the alphabetical open-project view). Use top to choose a
total result cap from 1 through 200, orderby to override the sort, and skip
for pagination on the general collection tools.
Example usage in Claude
"Show all unpaid invoices"
"List time entries for project 456 this month"
"Get all payments received from client 123 in 2024"
"Show trust account activity for client 789"
"List open projects ordered by project name"
Security note
API key in URL path. Bill4Time's API design embeds the API key directly as a path segment in every request URL (/b4t-api/{api_key}/v1/...). This is a Bill4Time API architecture constraint. The MCP resolves the key from the OS keyring, process environment, or a local file fallback; it never logs the key. However, the key-in-URL design has the following implications you should be aware of:
Server/proxy access logs on any machine between your client and Bill4Time's servers will record the full request URL, including the API key, for the duration of their log retention policy.
Network monitoring tools that capture request URLs (e.g. HTTP proxies, security appliances, debugging tools) will expose the key in logged URLs.
If your key is compromised, all API access to your Bill4Time account — billing data, client records, invoices, payments — is accessible until the key is rotated.
Recommended practices:
Rotate your API key periodically (quarterly at minimum) from Settings → API in your Bill4Time account.
Keep access logs on this machine private — ensure
~/.bill4time-mcp/is not world-readable, and that any HTTP proxy or network capture tool running on this machine is restricted to authorised users.Rotate immediately if you suspect the key has been exposed (e.g. via a shared log file, a network trace, or an accidental
curl -vpaste).Use least-privilege — if Bill4Time offers read-only API keys in the future, prefer those for this MCP (all current tools are read-only).
Testing
The test suite runs offline and needs no Bill4Time account: every Bill4Time API call is answered by an in-process stub session or by a stub HTTP server on 127.0.0.1. It covers list tool arguments and limits, error handling, credential file handling, the setup and verify commands, the stdio server, and the Streamable HTTP transport including the 2026-07-28 wire format, Host and Origin checks and stateless requests.
uv sync --locked
uv run --locked pytest -qCI runs the suite on every push and pull request to main.
License
MIT. See LICENSE.
Available Tools
59 toolsget_clientB
Get a client by ID.
Inputs: client_id (required). Returns: result
get_contactB
Get a contact by ID.
Inputs: contact_id (required). Returns: result
get_expenseC
Get an expense by ID.
Inputs: expense_id (required). Returns: result
get_invoiceC
Get an invoice by ID.
Inputs: invoice_id (required). Returns: result
get_paymentC
Get a payment by ID.
Inputs: payment_id (required). Returns: result
get_payment_appliedC
Get a payments-applied record by ID.
Inputs: record_id (required). Returns: result
get_projectC
Get a project by ID.
Inputs: project_id (required). Returns: result
get_time_entryB
Get a time entry by ID.
Inputs: entry_id (required). Returns: result
get_trust_recordB
Get a trust accounting record by ID.
Inputs: record_id (required). Returns: result
get_userB
Get a user by ID.
Inputs: user_id (required). Returns: result
list_active_clientsC
List all active clients.
Inputs: top, orderby. Returns: result
list_active_contactsC
List all active contacts.
Inputs: top. Returns: result
list_clientsC
List clients. Use filter_expr for OData filtering, e.g. "status eq 'Active'".
Inputs: top, skip, select, orderby, +1 more. Returns: result
list_closed_projectsC
List all closed projects.
Inputs: top, orderby. Returns: result
list_contact_connectionsC
List all contact connections. Use filter_expr for OData filtering.
Inputs: top, filter_expr. Returns: result
list_contact_connections_for_clientC
List all contact connections for a specific client.
Inputs: client_id (required). Returns: result
list_contact_connections_for_contactA
List all client/project connections for a specific contact.
Inputs: contact_id (required). Returns: result
list_contact_connections_for_projectC
List all contact connections for a specific project.
Inputs: project_id (required). Returns: result
list_contactsC
List contacts. Use filter_expr for OData filtering.
Inputs: top, skip, select, orderby, +1 more. Returns: result
list_contacts_for_date_rangeB
List contacts created within a date range (YYYY-MM-DD format).
Inputs: end_date (required), start_date (required), top. Returns: result
list_disabled_clientsC
List all disabled clients.
Inputs: top. Returns: result
list_expensesC
List expenses. Use filter_expr for OData filtering.
Inputs: top, skip, select, orderby, +1 more. Returns: result
list_expenses_for_clientC
List all expenses for a specific client.
Inputs: client_id (required), top. Returns: result
list_expenses_for_date_rangeC
List expenses within a date range (YYYY-MM-DD format).
Inputs: end_date (required), start_date (required), top. Returns: result
list_expenses_for_invoiceC
List all expenses attached to a specific invoice.
Inputs: invoice_id (required). Returns: result
list_expenses_for_projectC
List all expenses for a specific project.
Inputs: project_id (required), top. Returns: result
list_finalized_invoicesC
List all finalized invoices.
Inputs: top. Returns: result
list_invoicesC
List invoices. Use filter_expr for OData filtering.
Inputs: top, skip, select, orderby, +1 more. Returns: result
list_invoices_for_clientB
List all invoices for a specific client.
Inputs: client_id (required), top. Returns: result
list_invoices_for_date_rangeC
List invoices within a date range (YYYY-MM-DD format).
Inputs: end_date (required), start_date (required), top. Returns: result
list_invoices_for_projectB
List all invoices for a specific project.
Inputs: project_id (required), top. Returns: result
list_open_projectsC
List all open projects.
Inputs: top, orderby. Returns: result
list_paid_invoicesB
List all fully paid invoices.
Inputs: top. Returns: result
list_partially_paid_invoicesC
List all partially paid invoices.
Inputs: top. Returns: result
list_paymentsC
List payments. Use filter_expr for OData filtering.
Inputs: top, skip, select, orderby, +1 more. Returns: result
list_payments_appliedC
List payments-applied records. Use filter_expr for OData filtering.
Inputs: top, skip, select, orderby, +1 more. Returns: result
list_payments_applied_for_date_rangeB
List payments-applied within a date range (YYYY-MM-DD format).
Inputs: end_date (required), start_date (required), top. Returns: result
list_payments_applied_for_invoiceA
List all payment applications for a specific invoice.
Inputs: invoice_id (required). Returns: result
list_payments_applied_for_paymentC
List all invoice applications for a specific payment.
Inputs: payment_id (required). Returns: result
list_payments_for_clientB
List all payments for a specific client.
Inputs: client_id (required), top. Returns: result
list_payments_for_date_rangeC
List payments within a date range (YYYY-MM-DD format).
Inputs: end_date (required), start_date (required), top. Returns: result
list_payments_for_projectB
List all payments for a specific project.
Inputs: project_id (required), top. Returns: result
list_prebill_invoicesC
List all invoices in prebill status.
Inputs: top. Returns: result
list_projectsC
List projects. Use filter_expr for OData filtering, e.g. "status eq 'Open'".
Inputs: top, skip, select, orderby, +1 more. Returns: result
list_projects_by_billing_methodB
List projects by billing method. Values: Hourly, Flat Fee, Percentage.
Inputs: billing_method (required), top. Returns: result
list_projects_for_clientB
List all projects for a specific client.
Inputs: client_id (required), top. Returns: result
list_time_entriesC
List time entries. Use filter_expr for OData filtering.
Inputs: top, skip, select, orderby, +1 more. Returns: result
list_time_entries_by_billing_statusC
List time entries by billing status. Values: Ready For Summary, Ready For Billing, Billing Complete, Pending Project Close.
Inputs: billing_status (required), top. Returns: result
list_time_entries_for_clientA
List all time entries for a specific client.
Inputs: client_id (required), top. Returns: result
list_time_entries_for_date_rangeB
List time entries within a date range (YYYY-MM-DD format).
Inputs: end_date (required), start_date (required), top. Returns: result
list_time_entries_for_invoiceA
List all time entries attached to a specific invoice.
Inputs: invoice_id (required). Returns: result
list_time_entries_for_projectC
List all time entries for a specific project.
Inputs: project_id (required), top. Returns: result
list_time_entries_for_userC
List all time entries for a specific user.
Inputs: user_id (required), top. Returns: result
list_trust_recordsC
List trust accounting records. Use filter_expr for OData filtering.
Inputs: top, skip, select, orderby, +1 more. Returns: result
list_trust_records_for_clientB
List all trust accounting records for a specific client.
Inputs: client_id (required), top. Returns: result
list_trust_records_for_date_rangeB
List trust records created within a date range (YYYY-MM-DD format).
Inputs: end_date (required), start_date (required), top. Returns: result
list_trust_records_for_projectC
List all trust accounting records for a specific project.
Inputs: project_id (required), top. Returns: result
list_unpaid_invoicesC
List all unpaid invoices.
Inputs: top. Returns: result
list_usersC
List users. Use filter_expr for OData filtering.
Inputs: top, filter_expr. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 59 tools
Every tool targets a distinct entity or filter combination. The 'get_' and 'list_' prefixes clearly separate single-record retrieval from multiple-record listing, and the various 'list_*_for_*' tools are explicitly scoped to specific relationships, eliminating ambiguity.
All tools follow a consistent 'verb_noun' pattern, with 'get_' for individual records and 'list_' for collections. Suffixes like '_for_client' or '_for_date_range' are uniformly applied, making the naming predictable and easy to navigate.
With 59 tools, the set is excessively large. Many 'list_' variants (e.g., list_active_clients, list_disabled_clients) could be consolidated into a single list_clients with a filter parameter, making the surface unnecessarily bloated for the apparent scope.
The tool set is entirely read-only, lacking any create, update, or delete operations. Such a critical gap for a billing/time-tracking domain means agents cannot perform essential workflows like creating invoices or recording payments, severely limiting usefulness.
Maintenance
Related MCP Connectors
Clio MCP server for ChatGPT, Claude and any AI agent: search matters, log time entry, create task, add matter note, create calendar entry and list bills in your own Clio account. Cotlet works in the account through your Cloud Browser, a real Chrome where you sign in to Clio yourself; Cotlet does not store your password and you need no Clio API key. About the official Clio API: Yes. Clio has a REST API, the Clio Manage API v4, with OAuth 2.0 (checked 6 Nov 2026). Setup and FAQ: https://cotlet.dev/integrations/clio-api-mcp-server
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
Law firm management MCP: manage cases, clients, tasks, calendar and documents via Claude AI.
Invoicing you drive by talking to your AI: log time, raise invoices and track what's owed via MCP.
Related MCP Servers
- AlicenseCqualityBmaintenanceMCP server for Filevine that enables natural language interaction with legal case management, including projects, contacts, tasks, documents, billing, and more via Claude Desktop.10020 PyPI5MIT
- AlicenseAqualityCmaintenanceMCP server that connects Clio Manage to Claude via OAuth, enabling law firm management tasks like matter lookup, time tracking, billing, calendar, and document retrieval through natural language.41MIT
- AlicenseCqualityBmaintenanceMCP server providing full Smokeball API coverage for law firm practice management, enabling natural language interactions with matters, contacts, tasks, billing, and more via Claude Desktop.1001MIT
- AlicenseAqualityBmaintenanceMCP server for EZ@Work — manage clients, projects, time entries, and invoices from Claude through natural conversation. OAuth 2.1 with Google sign-in, multi-currency support.5MIT