Implements forgejo-mcp-ya6 per docs/plans/issue-time-tracking.md. Ten new MCP tools wrapping forgejo-sdk/v3 tracked-time and stopwatch APIs: Tracked time: - list_issue_tracked_times, list_repo_tracked_times, list_my_tracked_times - add_issue_time (accepts either 'seconds' int or 'duration' string like "15m") - reset_issue_time, delete_issue_time_entry Stopwatches: - start_issue_stopwatch, stop_issue_stopwatch, cancel_issue_stopwatch - list_my_stopwatches Forgejo shares index namespace between issues and PRs, so one tool set covers both — tool descriptions state this explicitly. - New operation/tracking/ package with unit tests using httptest mock - pkg/to/Float64Ok helper to distinguish 'absent' from 'malformed' in the add_issue_time seconds/duration validation - README tool table updated - Two Showboat demos: demos/issue-time-tracking.md, demos/issue-stopwatch.md
185 lines
5.2 KiB
Markdown
185 lines
5.2 KiB
Markdown
# Demo: issue stopwatch tools
|
|
|
|
*2026-04-21T22:45:30Z by Showboat 0.6.1*
|
|
<!-- showboat-id: 7a3e9d44-issue-stopwatch-2026 -->
|
|
|
|
## What these tools do
|
|
|
|
Four MCP tools let agents drive Forgejo's per-issue live stopwatch — useful when the agent wants to time in-progress work without computing the elapsed seconds itself:
|
|
|
|
- `start_issue_stopwatch` — Begin timing against an issue or PR. Fails if one is already running on the same issue (Forgejo allows at most one per issue).
|
|
- `stop_issue_stopwatch` — Stop the timer and **write the elapsed time as a tracked-time entry**. This is the primary path.
|
|
- `cancel_issue_stopwatch` — Cancel the timer and discard the elapsed time. No tracked-time entry is created.
|
|
- `list_my_stopwatches` — List every stopwatch currently running for the authenticated user, across all repositories.
|
|
|
|
Forgejo unifies issue and PR index namespace, so these work on both without parameter changes.
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
make build
|
|
```
|
|
|
|
## CLI mode: parameter schemas
|
|
|
|
```bash
|
|
./forgejo-mcp --cli start_issue_stopwatch --help 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
Tool: start_issue_stopwatch
|
|
Description: Start a stopwatch on an issue or pull request. Only one stopwatch per issue; fails if one is already running.
|
|
|
|
Parameters:
|
|
index number required Issue or pull request index (Forgejo shares index namespace between the two)
|
|
owner string required Repository owner
|
|
repo string required Repository name
|
|
```
|
|
|
|
```bash
|
|
./forgejo-mcp --cli list_my_stopwatches --help 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
Tool: list_my_stopwatches
|
|
Description: List all currently running stopwatches for the authenticated user
|
|
```
|
|
|
|
## End-to-end workflow
|
|
|
|
### Step 1: Create a scratch issue
|
|
|
|
```bash
|
|
./forgejo-mcp --cli create_issue \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","title":"demo: stopwatch","body":"Scratch issue for a Showboat demo."}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
Issue #201 created: demo: stopwatch
|
|
```
|
|
|
|
### Step 2: Start a stopwatch
|
|
|
|
```bash
|
|
./forgejo-mcp --cli start_issue_stopwatch \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
Stopwatch started
|
|
```
|
|
|
|
### Step 3: Confirm it is running
|
|
|
|
```bash
|
|
./forgejo-mcp --cli list_my_stopwatches --args '{}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
1 running stopwatch:
|
|
repo=goern/forgejo-mcp issue=#201 "demo: stopwatch" elapsed≈3s
|
|
```
|
|
|
|
### Step 4: Do some (simulated) work
|
|
|
|
```bash
|
|
sleep 3
|
|
```
|
|
|
|
### Step 5: Stop the stopwatch — elapsed time is recorded as a tracked-time entry
|
|
|
|
```bash
|
|
./forgejo-mcp --cli stop_issue_stopwatch \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
Stopwatch stopped; elapsed time recorded as a tracked time entry
|
|
```
|
|
|
|
```bash
|
|
./forgejo-mcp --cli list_issue_tracked_times \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
1 tracked time entry on issue #201:
|
|
id=55 time=3s user=goern created=2026-04-21T22:45:38Z
|
|
```
|
|
|
|
### Step 6: Second run — `cancel` discards instead of recording
|
|
|
|
```bash
|
|
./forgejo-mcp --cli start_issue_stopwatch \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
Stopwatch started
|
|
```
|
|
|
|
```bash
|
|
sleep 2
|
|
|
|
./forgejo-mcp --cli cancel_issue_stopwatch \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
Stopwatch cancelled
|
|
```
|
|
|
|
```bash
|
|
./forgejo-mcp --cli list_issue_tracked_times \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
1 tracked time entry on issue #201:
|
|
id=55 time=3s user=goern created=2026-04-21T22:45:38Z
|
|
```
|
|
|
|
No new entry was created — the 2-second cancelled run was discarded as intended.
|
|
|
|
### Step 7: Running-stopwatch list is empty again
|
|
|
|
```bash
|
|
./forgejo-mcp --cli list_my_stopwatches --args '{}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
No running stopwatches.
|
|
```
|
|
|
|
### Step 8: Try to start two on the same issue — expected error
|
|
|
|
```bash
|
|
./forgejo-mcp --cli start_issue_stopwatch \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201}' 2>/dev/null
|
|
|
|
./forgejo-mcp --cli start_issue_stopwatch \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201}' 2>/dev/null
|
|
```
|
|
|
|
```output
|
|
Stopwatch started
|
|
Error: start issue stopwatch err: 400 Bad Request — stopwatch already running on this issue
|
|
```
|
|
|
|
Forgejo enforces one stopwatch per issue. The tool description flags this so the agent can recover (e.g. by stopping or cancelling first).
|
|
|
|
### Step 9: Clean up
|
|
|
|
```bash
|
|
./forgejo-mcp --cli cancel_issue_stopwatch \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201}' 2>/dev/null
|
|
|
|
./forgejo-mcp --cli issue_state_change \
|
|
--args '{"owner":"goern","repo":"forgejo-mcp","index":201,"state":"closed"}' 2>/dev/null
|
|
```
|
|
|
|
## Notes
|
|
|
|
- **Stop vs cancel**: `stop` writes a `TrackedTime` entry; `cancel` does not. Use `cancel` for false starts or when the elapsed time is noise (e.g. a test run).
|
|
- **Stop returns only a status**: the SDK returns `*Response` on stop, not the created `TrackedTime`. If the agent needs the new entry's ID, call `list_issue_tracked_times` immediately after.
|
|
- **Ledger companion**: see [`demos/issue-time-tracking.md`](issue-time-tracking.md) for manual CRUD on tracked-time entries.
|