it-department
Sends notifications to a Discord webhook when the monitor escalates an incident or needs human attention.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@it-departmentcheck fleet health and list any open incident tickets"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
it-desk
A self-healing monitor and ticket desk for a fleet of local HTTP services on one Windows machine. It probes each service, restarts the ones that fail, verifies that exactly one process is serving the port afterwards, records every step as a ticket with evidence, asks a model for a diagnosis when scripted restarts stop working, and notifies a human. Coding agents reach it through an MCP server so they can check health, file bugs, request restarts and work a shared ticket queue instead of killing processes themselves.
It is a single FastAPI process (run.py) with a SQLite store, a static dashboard, and a stdio MCP
adapter (mcp_server.py).
Architecture
probe (HTTP, bounded timeout)
-> state machine per service (GREEN / RED / BREAKER, startup grace, consecutive-red gate)
-> diagnose (process dead, orphan listener, zombie duplicates, hang)
-> restart: drain_url (optional) -> exclusive restart lease (optional broker)
-> kill by command-line regex (denylist enforced) -> launch from registry
-> verify listener + probe
-> ticket with timeline events (probe_fail, diagnosis, kill, launch, verify_ok/verify_fail, ...)
-> recovered: ticket auto_resolved
-> scripts give up (verify fails, breaker trips, check_only service down): escalate
-> AI triage (structured JSON first, prose fallback; diagnosis only, never executed)
-> notification (Discord webhook and/or signed off-site heartbeat)
-> humans or agents claim / release / resolve tickets through the dashboard or MCPMain modules:
Module | Role |
| Poll loop, per-service state machine, restart policy, breaker, escalation |
| HTTP health probes |
| Tier-1 classification before anything is touched |
| The only action surface: drain, lease, kill-by-match, launch, verify |
| Process and listener helpers; the never-kill denylist lives here in code |
| SQLite (WAL) store: tickets, events, probe history, restart log, playbooks |
| Tier-2 model diagnosis; output is attached to the ticket only |
| Catalog of verbs a model may select; model-selected actions are recorded, only the monitor executes restarts |
| Groups 3+ services failing within 120 s into one fleet event |
| Optional signed heartbeat to an off-site watchdog (dead-man alerting) |
| Optional plain-language explanation feed over open tickets |
| Optional persistent Claude Code session in a ConPTY, shared over a WebSocket |
| Secret scrubber applied to every off-machine or untrusted-text sink |
| Dashboard and JSON API |
| stdio MCP server proxying to the desk |
More detail: docs/ARCHITECTURE.md.
The state-0 listener problem
Restart verification asks "is exactly one live process serving this port?" (procs.verify_listener).
On some Windows builds a healthy uvicorn listener is reported with TCP state 0 (psutil status
NONE): netstat prints no LISTENING row and Get-NetTCPConnection shows a blank state. A
filter on LISTEN therefore sees no owner for a working service, every restart is recorded as
failed, and the failures trip the circuit breaker.
procs.port_owners counts any socket bound to a loopback or wildcard address on the port with no
remote end and an owning PID that is alive, whatever state it reports. Connected sockets
(ESTABLISHED, TIME_WAIT) and binds on other addresses (for example a VPN proxy re-listening on
the same port) are ignored. verify_listener then returns:
pid-bound: exactly one owner and the probe passed;http: no socket row visible but the probe passed;failure when the probe failed or two or more processes own the port (zombie duplicates).
The netstat fallback (used only when psutil cannot read the table) is still blind to state-0
rows. Tests: tests/test_listener_probe.py.
Related MCP server: DevContext
MCP tools
Register: claude mcp add -s user it-department -- py -3.12 <path-to-checkout>/mcp_server.py
Tool | Purpose |
| Fleet health plus open ticket counts by kind |
| File a bug the agent could not fix (merged with an open ticket of the same normalised title, reopened if closed within 7 days) |
| Request a coordinated restart (drain, lease, kill, relaunch, verify, ticketed) |
| Open an investigation ticket with evidence |
| List tickets ready to work |
| Take or hand back a ticket |
| Close a claimed ticket with a resolution |
Writes carry a session id (CLAUDE_CODE_SESSION_ID or a random one). Inside the prime terminal
(IT_PRIME=1) writes are stamped as the prime, and the desk refuses restarts from that source.
Running
Requirements: Windows, Python 3.12.
py -3.12 -m pip install --require-hashes -r requirements.lock
copy config\services.example.yaml config\services.yaml (then edit)
mkdir %USERPROFILE%\.it-desk
py -3.12 -c "import secrets; print(secrets.token_urlsafe(32))" > %USERPROFILE%\.it-desk\token
py -3.12 run.pyThe dashboard is at http://127.0.0.1:8107. The API and dashboard accept loopback Host headers only,
and every write needs Authorization: Bearer <token>. With no token configured, writes fail closed.
Configuration
Environment
Variable | Default | Meaning |
|
| Service registry path |
|
| Settings path |
|
| SQLite DB ( |
|
|
|
|
| Bearer token file (read by the desk and the MCP server) |
| unset | Token fallback when the file is absent |
|
| Desk URL used by |
|
| Optional lease broker for exclusive restarts; unreachable = leaseless |
|
| DPAPI-encrypted HMAC key for the heartbeat |
|
| Markdown mirror of bug tickets ( |
| unset | Optional log of an external orphan-MCP reaper; feeds the MCP-zero alert and |
|
| Where Claude Code writes session transcripts; the prime terminal reads its own transcript there for context usage |
| unset | Optional JSONL audit log for the consultation-storm detector; unset = off |
| unset | Optional |
| unset | Key for the built-in DeepSeek provider |
| unset | Optional agent-run conductor whose approval gates appear in the explain feed |
| unset | DB path for |
| unset | Set to |
config/services.yaml
A list under services:. See config/services.example.yaml. Validation is in
app/config.py:_validate_services and runs on every save through the API.
Key | Meaning |
| Unique id |
| Display name |
|
|
| Port verified after a restart. Omit on |
| Health URL; must start with |
| Expected HTTP status and timeout |
| argv list; |
| Absolute working directory |
| Optional |
| Regex over the process command line; at least 6 characters, must contain a backslash or |
| Optional; POSTed before a kill so the service can finish in-flight work (15 s deadline) |
| Probe failures within this window after a launch are ignored |
| Parent id; a child failing while its parent is down is grouped under the parent |
|
|
config/settings.yaml
Key | Default | Meaning |
| 15 | Probe interval (minimum 3) |
| 2 | Consecutive probe failures before a service goes RED |
| 8 | Wait before verifying a relaunch |
| 7 | Probe history retention |
| 30 | An incident open this long, given up on by the monitor, appears in the work queue |
| 30 | A bug linked to a service closes after the service has been green this long |
| 4 | Restart budget per service; exceeding it opens the breaker and escalates |
| 30 | Breaker open time |
| 60 / 900 | Backoff between restarts of one service |
| 600 | Green time that resets the backoff |
|
| Per-service RED ticks required before the first restart |
|
|
|
|
| Command for the |
| 180 | Provider timeout |
| 1 | Triage runs per ticket |
|
| Notification webhook; blank = no-op |
|
| Events that notify |
|
| Bind address; host must stay loopback |
| off | Signed off-site heartbeat; see |
|
| Monitor loop paused (no probes, no restarts) |
| on, 10, 5, 300 | Consultation-storm detector (needs |
| on, 3600 | Notes Claude Code CLI version changes on matching tickets |
Incident statistics
py -3.12 scripts/stats.py [--db PATH] [--days N]Prints incident tickets opened in the window, recovered without escalation, escalated, went green after escalation, restart budget exhausted, closed with a written resolution, still open, median and p90 time from open to recovery without escalation, and a per-service breakdown. Bugs and investigations filed by agents are not counted. The database is opened read-only.
Measured figures
Figures measured on the author's own fleet over 114 days (2026-07-05 to 2026-10-26)
with scripts/stats.py; the database is not published.
Measure | Value |
Incidents opened | 1543 |
Recovered without escalation (green again before any escalation; includes restarts and transient blips) | 1123 (72.78%) |
Escalated (an | 407 (26.38%) |
Escalated, then went green again (automatic or manual) | 405 (26.25%) |
Restart budget exhausted (breaker opened) | 4 (0.26%) |
Closed with a written resolution by a person or agent | 11 (0.71%) |
Open to recovered without escalation, median / p90 | 16 s / 58 s |
Most escalations come from check_only services, which are never restarted and escalate on first
failure; one such service accounts for 338 of the 407.
Threat model
The desk assumes a single trusted local user.
Read routes (
GET /api/status,/api/tickets,/api/bugs,/api/settingsand similar) are unauthenticated local reads. They expose ticket text, error output and settings (the Discord webhook URL is redacted). Anything that can reach127.0.0.1:8107with a loopback Host header can read them.GET /api/registryand the prime routes require the token.A bearer token holder can change
config/services.yaml(launch.command,launch.cwd) andtriage.claude_cmdthrough the API. That is authenticated local code execution, by design: the desk exists to launch processes.The token is a file in the user profile (
IT_DESK_TOKEN_FILE). Any process running as that user can read it.The prime-source check (
X-IT-Source: primeheader orsource/whobody fields) is a convenience guard that stops the prime session from requesting restarts. It is not a security boundary; any token holder can omit it.Ticket text is treated as untrusted: it is fenced and scrubbed before it reaches a model, and the default triage CLI runs with no tools.
Tests
py -3.12 -m pip install -r requirements-dev.txt
py -3.12 -m pytest -qtests/conftest.py redirects every data, log and config path to a temporary directory and fails
the session if anything writes under the repo's data/, logs/ or config/. Cross-checks that
need the optional winspawn spawn auditor are skipped when it is not installed. CI runs on
windows-latest.
Limitations
Windows only: process handling, ConPTY (
pywinpty), DPAPI secret storage,CREATE_NO_WINDOWspawns and the listener semantics above are Windows-specific.requirements.lockpinspywinpty, so it does not install on Linux.Managed services must be Python processes launched from the registry;
kill_matchselects processes by command line.One machine. Probes must target
127.0.0.1; the dashboard binds loopback only.AI triage is advisory. Model-selected actions are recorded; only the monitor executes restarts.
The prime terminal needs the Claude Code CLI on PATH,
~/.it-desk/prime_mcp.jsonand~/.it-desk/prime_settings.json.The bug mirror writes
BUGS.mdintoIT_DESK_BUGS_DIRon every bug write.
License
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
A hosted backlog coding agents claim work from: one ticket per agent, finished work back to review.
- HydrantOAuthdev.hydrant
Track issues, projects and dependencies with your agent in a workspace you control.
- ParleyOAuthdev.weldra
Coordination hub for AI coding agents: message teammates, ask humans, audit every event.
- SuperlogOAuthsh.superlog
Open-source agent that observes and fixes your application. Query logs, traces, metrics, incidents.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceA self-hosted work dashboard for AI agent teams, where agents create and manage tickets through MCP tools and humans review an awaiting-human queue backed by an append-only event log. Enables ticket lifecycle management and evidence-based provenance tracking.MIT
- AlicenseNot gradedqualityBmaintenanceEnables coding agents to access live runtime observability data such as logs, deploys, and health metrics for evidence-based incident triage.1MIT
- AlicenseNot gradedqualityCmaintenanceEnables multiple AI coding agents to coordinate on a shared software project by registering, claiming tasks, declaring file intents, publishing structured change reports, and handing off context, with a local dashboard showing state in near real time.MIT
- AlicenseNot gradedqualityAmaintenanceLocal OS for your AI Agents fleets. ——————- Enables AI agents to coordinate through a durable local board with shared state, ticket lifecycle, evidence-based approvals, and journal-woken handoffs.2Apache 2.0