Add /unregister endpoint and document Claude Code hooks for graceful shutdown

This commit is contained in:
StillHammer 2026-01-25 11:06:51 +07:00
parent 66e5c677ea
commit 99cc1853aa
3 changed files with 67 additions and 0 deletions

1
.gitignore vendored
View File

@ -2,5 +2,6 @@ node_modules/
.state/ .state/
conversations/ conversations/
data/ data/
drafts/
*.log *.log
*.db *.db

View File

@ -169,6 +169,7 @@ mcp-claude-duo/
| Endpoint | Method | Description | | Endpoint | Method | Description |
|----------|--------|-------------| |----------|--------|-------------|
| `/register` | POST | Register a partner | | `/register` | POST | Register a partner |
| `/unregister` | POST | Unregister / go offline |
| `/talk` | POST | Send a message | | `/talk` | POST | Send a message |
| `/listen/:partnerId` | GET | Long-poll for messages | | `/listen/:partnerId` | GET | Long-poll for messages |
| `/conversations` | POST | Create group conversation | | `/conversations` | POST | Create group conversation |
@ -197,6 +198,42 @@ See [docs/db-schema.md](docs/db-schema.md) for full schema documentation.
| `BROKER_PORT` | `3210` | Broker listen port | | `BROKER_PORT` | `3210` | Broker listen port |
| `PARTNER_NAME` | `Claude` | Display name for the partner | | `PARTNER_NAME` | `Claude` | Display name for the partner |
### Graceful Shutdown with Hooks
To properly mark your Claude instance as offline when the MCP stops, configure a Claude Code hook.
**1. Create a settings file (if not exists):**
Create or edit `~/.claude/settings.json`:
```json
{
"hooks": {
"Stop": [
{
"matcher": "duo-partner",
"hooks": [
{
"type": "command",
"command": "curl -X POST http://localhost:3210/unregister -H \"Content-Type: application/json\" -d \"{\\\"partnerId\\\": \\\"$PARTNER_ID\\\"}\""
}
]
}
]
}
}
```
**2. Or use the Claude CLI:**
```bash
claude config set hooks.Stop '[{"matcher": "duo-partner", "hooks": [{"type": "command", "command": "curl -X POST http://localhost:3210/unregister -H \"Content-Type: application/json\" -d \"{\\\"partnerId\\\": \\\"YOUR_PROJECT_NAME\\\"}\""}]}]'
```
Replace `YOUR_PROJECT_NAME` with your actual partner ID (usually derived from your project folder name).
**Note:** Without this hook, partners will remain marked as "online" until the broker restarts or they reconnect.
## Contributing ## Contributing
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

View File

@ -434,6 +434,35 @@ app.post("/partners/:partnerId/notifications", (req, res) => {
res.json({ success: true }); res.json({ success: true });
}); });
/**
* Se désenregistrer / passer offline
* POST /unregister
* Body: { partnerId }
*/
app.post("/unregister", (req, res) => {
const { partnerId } = req.body;
if (!partnerId) {
return res.status(400).json({ error: "partnerId required" });
}
// Fermer la connexion long-polling si active
if (waitingPartners.has(partnerId)) {
const { res: waitingRes, heartbeat, timeout } = waitingPartners.get(partnerId);
clearInterval(heartbeat);
if (timeout) clearTimeout(timeout);
waitingPartners.delete(partnerId);
try {
waitingRes.json({ hasMessages: false, messages: [], reason: "unregistered" });
} catch {}
}
DB.setPartnerOffline(partnerId);
console.log(`[BROKER] Unregistered: ${partnerId}`);
res.json({ success: true });
});
/** /**
* Health check * Health check
*/ */