Skip to content

Commit 8d24f24

Browse files
authored
Merge pull request #1 from pinecode/feature/labor-costs-mcp-app
Add historical expenses and interactive MCP App reports
2 parents 122a9a7 + b2f2705 commit 8d24f24

16 files changed

Lines changed: 1748 additions & 160 deletions

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
node-version: [18, 20, 22]
14+
node-version: [20, 22, 24]
1515

1616
steps:
1717
- uses: actions/checkout@v4

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ dist/
33
.env
44
*.tsbuildinfo
55
test-all-tools.sh
6+
tmp/

‎CHANGELOG.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## 1.1.0
4+
5+
- Historical labor expense reports, rate histories, settings, CSV/PDF resources and opt-in rate/currency management.
6+
- Sandboxed MCP App for interactive inline dual-currency reports; text/JSON retained for non-UI hosts.
7+
- Read-only discovery tools for members, organization, project and currencies.
8+
- Fix report/list array filters to use the actual API parameter names, and format billing amounts in the organization currency instead of hard-coded EUR.
9+
- Resolve the caller via personal memberships, avoiding the first-page member-list limitation at startup.
10+
- Bound binary downloads and session-only export retention; no widget token, remote assets or telemetry.
11+
- Require Node 20+ for the pinned MCP Apps SDK.
12+
313
## 1.0.0 (2026-03-03)
414

515
### Features

‎README.md‎

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ MCP server for [SolidTime](https://www.solidtime.io/) — the open-source time t
77

88
## Features
99

10-
- **8 read-only tools by default**, or 22 tools with explicitly enabled writes
10+
- **18 read-only tools by default**, or 38 tools with explicitly enabled writes
1111
- **Start/stop timers** with automatic active-timer detection
1212
- **Aggregated reports** grouped by day, week, project, client, and more
1313
- **Auto member_id resolution** — no manual configuration needed
@@ -58,7 +58,7 @@ Get your API token from **SolidTime > Settings > API**.
5858
## Codex Configuration
5959

6060
Codex starts this server as a local stdio process. It does not need a public
61-
MCP URL or an open listening port. Use Node.js 18 or newer.
61+
MCP URL or an open listening port. Use Node.js 20 or newer.
6262

6363
1. Build the checkout as above.
6464
2. Activate personal API tokens on self-hosted Solidtime if necessary:
@@ -111,7 +111,7 @@ SOLIDTIME_READ_ONLY = "true"
111111
or Codex, then check `/mcp` and ask for a read-only report. Registration alone
112112
does not prove that authentication and tool calls work.
113113

114-
The default 8 tools cannot change data. Setting `SOLIDTIME_READ_ONLY=false`
114+
The default 18 tools cannot change data. Setting `SOLIDTIME_READ_ONLY=false`
115115
enables writes in the server; Codex's allowlist must also be changed to expose
116116
them. Only do that intentionally with an appropriately permissioned token.
117117

@@ -243,3 +243,123 @@ npm run inspector # Test with MCP Inspector
243243
## License
244244

245245
MIT
246+
# Employee Expenses and Inline Reports (1.2.0)
247+
248+
The PineCode Solidtime **0.21.1** extension adds historical employee expenses.
249+
It is separate from upstream customer billing: the original API `cost` field still
250+
means billable revenue. Standard tools continue to work on upstream Solidtime;
251+
expense tools require the extension and return a normal API error on older servers.
252+
253+
- `solidtime_get_labor_cost_report`: interactive MCP App plus structured JSON, with dates,
254+
employee/project/client/task/tag filters, grouping, display currency and pagination.
255+
- `solidtime_get_labor_cost_entries`: entry details, effective rates, both currencies,
256+
completeness and missing-data flags.
257+
- `solidtime_get_labor_cost_rates`: effective-dated employee/project history, including cancellations.
258+
- `solidtime_get_labor_cost_settings`: organization expense timezone, project currencies and scoped employee payment currencies.
259+
- `solidtime_get_current_labor_cost_rates`: today's personal rates in the organization timezone, scoped to the caller.
260+
- `solidtime_export_labor_costs`: dual-currency CSV/PDF as a private MCP resource link.
261+
- `solidtime_export_employee_report`: detailed employee PDF with Overview time charts,
262+
Project > Description breakdown, original earnings and payment estimate in the header.
263+
- `solidtime_list_members`, `solidtime_get_organization`, `solidtime_get_project`,
264+
`solidtime_get_currencies`: discover IDs and currencies without guessing.
265+
266+
With `SOLIDTIME_READ_ONLY=false`, Owner/Admin may also use
267+
`solidtime_create_labor_cost_rate`, `solidtime_update_labor_cost_rate`,
268+
`solidtime_cancel_labor_cost_rate`, `solidtime_update_expense_timezone` and
269+
`solidtime_update_project_expense_currency` and `solidtime_update_member_payment_currency`.
270+
The latter accepts `{id: "member-uuid", payment_currency: "RUB"}` (or `null` to
271+
preserve original rate currencies). It changes a current reporting preference for
272+
all periods, not hourly rates or actual settlements. Rate changes/cancellations can affect
273+
history: confirm the effective interval with the user before invoking them. The UI
274+
widget is read-only and does not expose these mutation controls.
275+
276+
Example: ask for one employee's monthly report with `currency: "RUB"`. The report
277+
shows original USD and historically converted RUB side by side. It uses official
278+
CBR rates on each entry's cost date, **not** the payday rate or a payroll ledger.
279+
Money stays in decimal strings; different original currencies are never added
280+
together. Missing rates or unsupported FX make the subtotal explicitly incomplete.
281+
282+
Payment currency is independent of both rate and display currency: an employee
283+
can earn USD, receive RUB, and still appear in a USD project report. The widget,
284+
structured JSON and CSV/PDF include a separate payment estimate and weighted FX
285+
from the API. Each earning uses its historical daily CBR rate. For example, 6 USD
286+
at 80 RUB/USD and 12 USD at 90 yields 18 USD, 1,560 RUB and 86.66666667 RUB/USD.
287+
The weighting uses unrounded earnings, not calendar days or hours; different
288+
currency pairs stay separate. Missing payment quotes have their own completeness
289+
flag and no invented average. Zero earnings have no weighted rate. On older
290+
extensions without payment fields, the widget shows payment data as unavailable
291+
instead of guessing the employee's payment currency.
292+
293+
For a monthly project/task breakdown, call `solidtime_get_labor_cost_report` with
294+
`date_from: "2026-09-01"`, `date_to: "2026-09-30"`, the employee's `member_ids`,
295+
`group: "project"`, `sub_group: "task"`, and `currency: "RUB"`.
296+
Dates are inclusive cost dates in the organization timezone. Each project and
297+
nested task shows billable hours, original expenses and converted expenses;
298+
parent and child subtotals must not be added together. Only completed billable
299+
work is included, not non-billable work, breaks or running timers. Use the standard
300+
time entry report when all tracked hours are needed.
301+
302+
For the detailed employee time-and-pay PDF, first resolve the **membership** UUID
303+
with `solidtime_list_members`, then call:
304+
305+
```json
306+
{
307+
"name": "solidtime_export_employee_report",
308+
"arguments": {
309+
"member_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
310+
"date_from": "2026-09-01",
311+
"date_to": "2026-09-30"
312+
}
313+
}
314+
```
315+
316+
The PDF includes the standard Overview time chart, project pie chart and
317+
Project > Description table, with original earnings and the employee's configured
318+
payment-currency estimate at the top. Dates are inclusive persisted cost dates.
319+
Optional project/client/task/tag filters and `billable` apply to both sections.
320+
Time includes all matching completed work; earnings still include only billable
321+
work. Breaks/running timers and billing rounding are excluded. Multiple rate
322+
currencies and incomplete rate/FX subtotals remain explicit. Owner/Admin may
323+
select any employee; other members may select only themselves, enforced by the API.
324+
325+
The result is a `resource_link` (`solidtime://exports/<random-id>`), not a public
326+
download URL. Read it with MCP `resources/read` before its ten-minute expiry to
327+
obtain the `application/pdf` base64 bytes, or discover active files through
328+
`resources/list`. A host can save those bytes locally or
329+
attach them to an explicitly authorized document/Telegram workflow. This server
330+
does not send reports to Telegram or any third-party service. The same PDF is
331+
available in Solidtime's Overview **Export > Employee PDF** menu after selecting
332+
one member. The deployed Solidtime extension must include `/labor-costs/employee-report`;
333+
older deployments return 404, not a substitute billing PDF.
334+
335+
The inline report keeps the group overview compact on phones. Entry details are
336+
collapsed initially, paginated by 20, and scrolling tables have a bounded height;
337+
the MCP Apps SDK reports actual content size to the host instead of reserving a
338+
full-height canvas. Group/subgroup, date and currency changes preserve the employee filter.
339+
340+
### MCP App and Security
341+
342+
The report tool advertises `ui://solidtime/labor-costs.html` using the standard
343+
[MCP Apps extension](https://modelcontextprotocol.io/extensions/apps/overview).
344+
Supporting desktop hosts render the widget inline: dates, currency, grouping,
345+
pages and CSV/PDF downloads stay interactive. Hosts without MCP Apps still receive
346+
the same structured JSON/text. Node **20+** is required; the compatible 1.x Apps SDK
347+
is pinned rather than migrating the existing MCP server to SDK 2.x.
348+
349+
The HTML/JavaScript bundle is served locally through MCP, with no CDN, analytics,
350+
direct API connection or token in the iframe. Its CSP declares empty network and
351+
external-resource origin lists. Data is inserted as text, never executable HTML.
352+
All API calls stay pinned to the configured HTTPS origin with redirects blocked;
353+
the server enforces role visibility. Export buffers are capped at 8 MiB and five
354+
files, expire after ten minutes, and live only in process memory. Restarting the
355+
MCP server removes them. Downloads are mediated by the host, not public URLs.
356+
357+
### Update an Existing Codex Connection
358+
359+
Rebuild with `npm ci && npm run build`, retain the existing Keychain launcher or
360+
your configured token injection, then restart **only the Solidtime MCP server**
361+
from Settings > MCP servers. No token belongs in source or command-line arguments.
362+
If your Codex configuration uses `enabled_tools`, add the read tool names above;
363+
otherwise an old allow list hides the new report tools. Keep `SOLIDTIME_READ_ONLY=true`
364+
for reporting. Reconnection and MCP Apps support depend on the host; a successful
365+
protocol test alone is not proof that an inline widget rendered.

0 commit comments

Comments
 (0)