worklog-mcp
# worklog-mcp
An MCP server that gives a coding agent somewhere to write down what it did and **why**.
Git already records what changed. What it doesn't record is the reasoning — why this
approach over the alternative, what was tried and abandoned, what's still broken, which
test run was green when the PR went up. That context lives in a chat transcript that
nobody reads again.
This server gives the agent six tools to record it as it works. By default it appends
JSONL to a file in your repo. Point it at an HTTP endpoint and it posts there instead.
```jsonc
// .mcp.json
{
"mcpServers": {
"worklog": {
"type": "stdio",
"command": "npx",
"args": ["-y", "github:aadityakushwaha/worklog-mcp"]
}
}
}
```
That's the whole setup. No account, no key, no server. Events land in
`.worklog/events.jsonl`.
## Tools
| Tool | When the agent calls it |
|---|---|
| `log_work` | finished something meaningful — the why and any open loops |
| `record_decision` | made an architecturally consequential choice (ADR-style, with alternatives and consequences) |
| `report_test_run` | ran a suite — result and counts, not full logs |
| `link_pr` | opened or merged a pull request |
| `update_progress` | set a work item's status: `not_started` / `in_progress` / `blocked` / `done` |
| `sync_doc` | pushed a plan, spec, progress doc, runbook or research note as rendered markdown |
Every event carries a `sessionId` shared across one agent run and a unique `id` per
event. The agent never passes either — they're threaded automatically, so one run's
events can be grouped afterwards and a retried call can be de-duplicated by the
receiver.
## Configuration
| Variable | Default | Meaning |
|---|---|---|
| `WORKLOG_FILE` | `.worklog/events.jsonl` | Where to append when there's no receiver |
| `WORKLOG_URL` | — | If set, POST events here instead of writing a file |
| `WORKLOG_API_KEY` | — | Bearer token. Required when `WORKLOG_URL` is set |
Add `.worklog/` to `.gitignore` unless you want the log committed. Some teams do —
it makes review of an agent's reasoning part of the PR.
## Reading the log
It's JSONL, so the usual tools work:
```bash
# what got done, most recent first
jq -r 'select(.tool=="log_work") | "\(.at) \(.summary)"' .worklog/events.jsonl | tail -20
# every architectural decision, with its alternatives
jq 'select(.tool=="record_decision") | {title, decision, alternatives}' .worklog/events.jsonl
# anything still blocked
jq 'select(.tool=="update_progress" and .status=="blocked")' .worklog/events.jsonl
```
## Sending somewhere else
Set `WORKLOG_URL` and every event is `POST`ed to `<WORKLOG_URL>/api/agent/events` with
`Authorization: Bearer <WORKLOG_API_KEY>`. The body is the event — `tool`, `id`,
`sessionId` when known, and the tool's own arguments:
```json
{ "tool": "log_work", "id": "9f2c…", "sessionId": "7a10…", "summary": "…", "intent": "…" }
```
Answer with:
```json
{ "success": true, "data": { "sessionId": "7a10…", "eventId": "…" } }
```
The `sessionId` in the **first** response is adopted and threaded into every later call,
so the receiver owns session identity rather than the client guessing at it. On
`{ "success": false, "error": "…" }` or a non-2xx, the tool call returns that error to
the agent.
That's the entire contract — a single endpoint. A receiver is an afternoon's work in
whatever you already run.
## Making it automatic
Logging that depends on the agent remembering to log is logging that stops after a
week. Wire it to a `Stop` hook so it fires at the end of every session:
```jsonc
// .claude/settings.json
{
"hooks": {
"Stop": [{ "matcher": "", "hooks": [{ "type": "command",
"command": "echo 'Before finishing: call log_work with what you did and why.'" }] }]
}
}
```
## Development
```bash
npm install
npm run build # tsc → dist/ (dist is committed so npx needs no build step)
npm test
```
## Licence
MIT
TDQS
Scored across 6 tools
Most tools target distinct actions (logging work, recording decisions, reporting test runs, linking PRs, updating progress, syncing docs). However, log_work and update_progress could be confused since both deal with work item status, though descriptions help clarify boundaries.
All tool names follow a consistent verb_noun snake_case pattern (log_work, record_decision, report_test_run, link_pr, update_progress, sync_doc). The naming is predictable and uniform, making it easy to infer function from name.
With 6 tools, the set is well-scoped for a worklog server. Each tool covers a distinct aspect of work logging without redundancy or excessive granularity, fitting comfortably within the ideal 3-15 range.
The tool set is heavily write-oriented (log, record, report, link, update, sync) but lacks any retrieval or query tools. There is no way to read/list/export the work log, which is a significant gap for a server whose purpose is to capture work history. This will likely cause agent failures when attempting to review or summarize logged work.