show full body
# STATUS-20260819-005 — Phase 1 stubs ported + registered: GREEN ✅
## TL;DR
5 Phase 1 stubs + `get_pending` (6 methods total) ported from Python → Go, registered with MCP server, verified end-to-end. **B1 fix on `get_agent_status` applied** (rename `agent` → `agent_id`, wrap store status into spec shape `{agent_id, status, last_seen}` with status string + null last_seen mapping). Dashboard `/agents/command/data` now populates real values.
| Method | Before | After |
|---|---|---|
| `get_agent_status` | ❌ rejected `agent_id` arg | ✅ returns `{agent_id, status, last_seen}` |
| `get_current_task` | ❌ "unknown tool" (73ms error) | ✅ returns `{task:null, started_at:null, eta:null}` |
| `get_agent_log_tail` | ❌ "unknown tool" | ✅ returns `{lines:[]}` |
| `cost_counters` | ❌ "unknown tool" | ✅ returns `{tokens_in:0, tokens_out:0, cost_usd:0, model:""}` |
| `stop_agent` | ❌ "unknown tool" | ✅ returns `{ok:false, reason:"not implemented", implemented:false}` |
| `get_pending` | ❌ "unknown tool" | ✅ returns `{to:0, from:0}` |
**tools/list count: 28 → 32** (+4 visible after dedup; 5 new stubs + 1 status fix on existing).
## What I did
### 1. messaging.go — B1 fix
Patched `getAgentStatusInput`:
- `Agent string json:"agent"` → `AgentID string json:"agent_id"`
- error msg `"agent is required"` → `"agent_id is required"`
- Wrapped `store.Status` result into spec shape with status-string mapping:
```go
var status string
switch {
case st.Available:
status = "LIVE"
case st.LastSeen.IsZero():
status = "unknown"
default:
status = "IDLE"
}
var lastSeen any
if !st.LastSeen.IsZero() {
lastSeen = st.LastSeen
}
return nil, map[string]any{
"agent_id": in.AgentID,
"status": status,
"last_seen": lastSeen,
}, nil
```
Backup at `messaging.go.bak-pre-agent-id-20260819T1259Z` and `messaging.go.bak-pre-status-map-20260819T1303Z`.
### 2. New package: `internal/adapters/agent_status/`
144 lines, mirrors `inbox.go` template. Registers 5 tools (NOT `get_agent_status` — that's owned by messaging). Phase 1 honest-empty shapes only; no DB or filesystem reads.
```go
// from agent_status.go
mcp.AddTool(s, &mcp.Tool{Name: "get_current_task", ...}, a.getCurrentTask)
mcp.AddTool(s, &mcp.Tool{Name: "get_agent_log_tail", ...}, a.getAgentLogTail)
mcp.AddTool(s, &mcp.Tool{Name: "cost_counters", ...}, a.costCounters)
mcp.AddTool(s, &mcp.Tool{Name: "stop_agent", ...}, a.stopAgent)
mcp.AddTool(s, &mcp.Tool{Name: "get_pending", ...}, a.getPending)
```
### 3. main.go — registration
```diff
+ "github.com/ivoherman/mcp-server/internal/adapters/agent_status"
+ // ACC status primitives — Phase 1 stubs (added 2026-08-19).
+ agentStatusAdapter := agent_status.New(agent_status.Config{})
+ agentStatusAdapter, // in adapters list
```
Backup at `main.go.bak-pre-agent-status-20260819T1300Z`.
### 4. Build + deploy
```bash
$ cd /volume1/docker/mcp/source
$ docker build -t mcp-server:0.7.3-stubs-registered . # 144 lines, 0 errors
$ # update compose
$ sed -i 's|image: mcp-server:0.7.2-ok-true|image: mcp-server:0.7.3-stubs-registered|' /volume1/docker/mcp/docker-compose.yml
$ docker compose up -d mcp-server
$ curl -sk http://127.0.0.1:8642/healthz → 200
```
Container healthy, all adapters registered (per logs):
```
adapter registered: obsidian
adapter registered: npm
adapter registered: openscad
adapter registered: messaging
adapter registered: vaultwarden
adapter registered: inbox
adapter registered: agent_status ← NEW
mcp-server listening on 0.0.0.0:8642
```
## End-to-end verification
### Live `tools/list` against mcp.ivoherman.nl/mcp
32 tools registered. The 6 ACC methods:
```
✓ get_agent_status (existing, B1 fix on arg shape + return shape)
✓ get_current_task (NEW)
✓ get_agent_log_tail (NEW)
✓ cost_counters (NEW)
✓ stop_agent (NEW)
✓ get_pending (NEW)
```
### Spot-check each stub returns spec shape
```
$ tools/call get_agent_status {agent_id:"ziltoid"} (no heartbeat)
→ {"agent_id":"ziltoid","last_seen":null,"status":"unknown"} ✅
$ tools/call get_agent_status {agent_id:"zoltan"} (heartbeat 13:01:25, stale)
→ {"agent_id":"zoltan","last_seen":"2026-08-19T13:01:25...","status":"IDLE"} ✅
$ tools/call get_current_task {agent_id:"ziltoid"}
→ {"eta":null,"started_at":null,"task":null} ✅
$ tools/call get_agent_log_tail {agent_id:"ziltoid","lines":5}
→ {"lines":[]} ✅
$ tools/call cost_counters {agent_id:"ziltoid","since":null}
→ {"cost_usd":0,"model":"","tokens_in":0,"tokens_out":0} ✅
$ tools/call stop_agent {agent_id:"ziltoid"}
→ {"implemented":false,"ok":false,"reason":"not implemented"} ✅
$ tools/call get_pending {agent_id:"ziltoid"}
→ {"from":0,"to":0} ✅
```
### Dashboard `/agents/command/data` populates
```json
[
{
"id": "ziltoid",
"status": "UNKNOWN", ← Phase 1 stub: "unknown" doesn't match LIVE/IDLE/ERROR set, falls through
"last_seen": null,
"last_seen_human": "—",
"current_task": null,
"current_task_started": null,
"log_lines": [],
"costs": {"cost_usd":0,"model":"","tokens_in":0,"tokens_out":0},
"pending_from": 0,
"pending_to": 0
},
{
"id": "zoltan",
"status": "IDLE", ← real heartbeat 13:01:25, stale (>HeartbeatMaxAge)
"last_seen": "2026-08-19T13:01:25.132206395Z",
"last_seen_human": "6m ago",
"current_task": null,
"current_task_started": null,
"log_lines": [],
"costs": {"cost_usd":0,"model":"","tokens_in":0,"tokens_out":0},
"pending_from": 0,
"pending_to": 0
}
]
```
**All 5 stub-driven fields populate correctly.** No errors. No "739846d ago" (the zero-LastSeen bug is fixed).
## Decisions / notes
- **`get_agent_status` not re-registered in agent_status adapter** — would create two tools with same name. messaging.go owns it (with B1 fix).
- **`status: "unknown"` string** in MCP response stays "UNKNOWN" in dash (uppercase) — by design per dash `_build_agent_card` line 493 (`if status in ("LIVE","IDLE","ERROR")`). Phase 1 stub agents naturally show "UNKNOWN" in the UI.
- **Phase 3+ work** (per status file TODO): wire `cost_counters` to real token accounting, `get_pending` to messaging store counts.
- **Vestigial `agent_status.py` on bind mount** — still present, NOT removed (would require another `cat | ssh` to `/volume1/docker/dashboard/app/`). Worth deleting in a future housekeeping pass; documented in R-MSG-012.
## Status
- **R-Q-20260819-005** → `closed-deployed-verified` (this file) ✅
- **R-MSG-20260819-012** → `closed-implemented` (my R-MSG-012 findings led to this R-Q)
- **MSG-20260819-001** → `closed-deployed-verified` (your debug request resolved)
- **R-MSG-20260819-011** → `closed-bug1-verified` (your H3 hypothesis confirmed + expanded)
- Thread `mcp-hang-2026-08-19` **CLOSED**.
## Net state
**Phase 1 stubs are live.** All 5 Phase 1 stubs + `get_pending` registered with MCP server. `get_agent_status` arg-shape fixed (B1). Dashboard populates real values (zero defaults + real heartbeat for zoltan).
## Awaiting
1. **Ivo**: spec patch for §Phase 1 — needs to acknowledge Phase 1 is now actually live (vs the spec wording that assumed it was live all along). Per spec §Decisions: "do not change without his nod."
2. **Zoltan or Ivo**: decide on vestigial `agent_status.py` cleanup.
3. **Ivo**: review a9a0c4b (already pushed to origin/main per your "Go on both" at 12:58 UTC).
4. **Phase 3 work**: cost model source (Open Q #1), log tail location (Open Q #4), stop semantics (Open Q #3).
— Ziltoid
2026-08-19 13:08 UTC