Bill4Time Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Bill4Time Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<p align="center"><a href="https://rosenadvertising.com/datalink/?utm_source=github&utm_medium=readme-banner&utm_campaign=bill4time-mcp"><img src="https://image.rosenadvertising.com/datalink/readme-banner-v2.png" alt="DataLink by Rosen Advertising: don't want to install and run this yourself? Get it hosted." width="100%"></a></p>
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.
filter_expr expressions to the general list tools for advanced queries.The server registers 59 tools, all of them read-only.
<details> <summary>All 59 tools</summary>
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</details>
The server also registers three prompts and three resources.
| Prompt | What it does |
|---|---|
client_billing_summary | Builds the full billing picture for one client: projects, time, invoices and payments. Takes client_id. |
trust_account_review | Reviews a client's trust accounting records for compliance. Takes client_id. |
unbilled_time_report | Reports unbilled time entries for a date range. Takes start_date and end_date. |
| Resource | What it provides |
|---|---|
bill4time://active_clients | Active clients. |
bill4time://users | Bill4Time users (the timekeeper list). |
bill4time://security-notes | Security notes for the server, including the API key in the URL path. |
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.
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 |
|---|---|---|---|
BILL4TIME_API_KEY | Yes (saved by setup) | Credential store | Bill4Time API key. |
BILL4TIME_MCP_USE_KEYRING | No | 1 | Set to 0 to skip the operating system keyring and use the environment and the .env file fallback. |
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.
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.
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 |
|---|---|---|
BILL4TIME_MCP_TRANSPORT | stdio | stdio or streamable-http. |
BILL4TIME_MCP_HOST | 127.0.0.1 | Bind address. 127.0.0.1, localhost and ::1 use the SDK's built-in Host and Origin checks; any other value requires BILL4TIME_MCP_ALLOWED_HOSTS. |
PORT | 8080 | Port; must be an integer. |
BILL4TIME_MCP_ALLOWED_HOSTS | unset | Comma-separated Host header values accepted on a non-loopback bind, such as mcp.example.com:8080 or mcp.example.com:*. |
BILL4TIME_MCP_ALLOWED_ORIGINS | unset | Comma-separated Origin values accepted on a non-loopback bind, such as https://client.example.com. Requests without an Origin header are accepted. |
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.
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 get_ tool that finds no matching record | "Bill4Time resource was not found. Check the requested record." |
| HTTP 429 | "Bill4Time rate limit reached. Retry after N seconds." where N comes from the Retry-After header, or "Retry later." when the header is missing or unusable. |
| 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.
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.
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.
"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"
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:
Recommended practices:
~/.bill4time-mcp/ is not world-readable, and that any HTTP proxy or network capture tool running on this machine is restricted to authorised users.curl -v paste).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.
MIT. See LICENSE.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.