Metadata-Version: 2.4
Name: vigil-mcp
Version: 0.2.1
Summary: Proactive memory layer for AI — the AI that reaches back out to you
Author-email: Zsolt Csaszti <prodbysilky.as@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/prodbysilky/vigil-mcp
Project-URL: Repository, https://github.com/prodbysilky/vigil-mcp
Project-URL: Bug Tracker, https://github.com/prodbysilky/vigil-mcp/issues
Keywords: mcp,ai,commitments,productivity,claude,reminders
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Office/Business :: Scheduling
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: mcp>=1.0.0
Provides-Extra: api
Requires-Dist: fastapi>=0.100.0; extra == "api"
Requires-Dist: uvicorn>=0.23.0; extra == "api"
Provides-Extra: push
Requires-Dist: pywebpush>=2.0.0; extra == "push"
Requires-Dist: cryptography>=41.0.0; extra == "push"
Provides-Extra: hosted
Requires-Dist: fastapi>=0.100.0; extra == "hosted"
Requires-Dist: uvicorn>=0.23.0; extra == "hosted"
Requires-Dist: pywebpush>=2.0.0; extra == "hosted"
Requires-Dist: cryptography>=41.0.0; extra == "hosted"
Requires-Dist: anthropic>=0.25.0; extra == "hosted"
Provides-Extra: notify
Requires-Dist: plyer>=2.1.0; extra == "notify"
Provides-Extra: all
Requires-Dist: fastapi>=0.100.0; extra == "all"
Requires-Dist: uvicorn>=0.23.0; extra == "all"
Requires-Dist: pywebpush>=2.0.0; extra == "all"
Requires-Dist: cryptography>=41.0.0; extra == "all"
Requires-Dist: anthropic>=0.25.0; extra == "all"
Requires-Dist: plyer>=2.1.0; extra == "all"

# 🔥 Vigil MCP

<!-- mcp-name: io.github.zsoltcsaszti/vigil-mcp -->

> The AI that reaches back out to you.

Most AI tools wait. You open them, you ask, they answer. Vigil flips that.

Connect it to Claude once — and it remembers what you said you'd do, reasons about when it's worth interrupting you, and follows up on its own.

---

## The problem

Every AI interaction ends the same way: you close the tab and the context disappears. The AI has no persistence, no agency, no follow-through. It's reactive by design.

Vigil is a layer that changes that.

---

## How it works

Vigil is an [MCP server](https://modelcontextprotocol.io) — a tool layer that any compatible AI can plug into. When connected:

1. **Claude detects commitments automatically** — "I'll come back tomorrow", "remind me to push this on Friday", "I'll do X after lunch" → saved instantly, no command needed
2. **A policy engine runs in the background** — every 60 seconds, it evaluates every pending commitment against 7 conditions before deciding whether to act
3. **When you open a new session**, Claude reads `vigil://pending` and opens with urgent items naturally — "Hey, you said you'd finish the README. Still on it?"
4. **Desktop and push notifications fire** when things are overdue and you haven't opened Claude yet
5. **Nudges go to Slack or Discord** if you've configured a webhook
6. **V3 integrations run every 5 minutes** — idle detection, adaptive learning, and weekly reports work silently in the background
7. **Every decision is logged and explainable** — ask "why did you message me?" and get the full trace

---

## Policy engine

Vigil doesn't act on a timer. It reasons.

Every potential nudge passes through a 7-step decision chain:

```
1. Hypothetical intent?              → never act
2. Grace period (<30 min old)?       → too fresh, skip
3. Early phase (>48h to deadline)?   → too soon, skip
4. Cooldown window active?           → avoid spam (4h / 1h / 2h by phase)
5. Daily attention budget exhausted? → suppress unless importance ≥ 0.8
6. Soft intent?                      → reduce effective importance by 0.2
7. Phase + importance threshold      → fire or stay silent
```

Every decision — including suppressed ones — is written to `decision_log` with phase, urgency, budget used, and full reasoning text.

### Temporal phases

| Phase | Window | Cooldown | Behavior |
|---|---|---|---|
| `early` | > 48h until due | — | No nudge |
| `execution` | 4–48h until due | 4h | Nudge if importance ≥ 0.6 |
| `risk` | < 4h until due | 1h | Always nudge |
| `overdue` | Past deadline | 2h | Escalate |

### Auto-decay

Nudged 3+ times with no response → importance decays 15% per cycle. After 6 ignored nudges → auto-archived. No noise, no spam.

---

## Features

| Feature | Description |
|---|---|
| 🧠 **Policy engine** | 7-step reasoning chain, not a simple timer |
| 💬 **Natural language input** | "remind me to push the PR before EOD" → parsed and saved automatically |
| 📬 **Proactive notifications** | Native macOS/Windows/Linux OR Web Push to phone/browser |
| 🔔 **Slack/Discord integration** | Nudges delivered to your Slack or Discord channel |
| 🤖 **Slack slash commands** | `/vigil remind me to X` creates a commitment from Slack |
| 📱 **Installable PWA** | Add dashboard to phone home screen — iOS 16.4+ / all Android |
| ☁️ **Hosted mode** | One-click Railway/Render deploy, auth-protected, multi-user |
| ✏️ **Edit commitments** | Update deadline, importance, intent, or description after saving |
| 🔁 **Recurring commitments** | Daily, weekly (specific days), monthly — auto-reschedules |
| 📉 **Auto-decay** | Importance drops on ignores; auto-archives after 6 nudges |
| 🌅 **Daily digest** | Optional morning summary notification |
| 🖥️ **Web dashboard** | Dark-theme UI at `localhost:7734` (or your hosted URL) |
| 🔍 **Full explainability** | Every decision logged with reasoning — "Why did you message me?" |
| 💤 **Idle detection** | Nudges escalate when no Claude session detected for 4+ hours |
| 📊 **Adaptive policy** | Learns your follow-through patterns per category, boosts flagging commitments |
| 📋 **Reliability reports** | Weekly Monday summary: follow-through % by category |

---

## Architecture

```
Claude Desktop
    │
    ├── Tools     (save / create_from_text / get / complete / snooze /
    │              update / explain / budget / digest / set_slack_webhook /
    │              set_discord_webhook / list_features / enable_feature /
    │              disable_feature)
    └── Resources (vigil://pending · vigil://status · vigil://history)

vigil-mcp process
    ├── server.py       — FastMCP server, tools, resources, background loop
    ├── policy.py       — Decision engine (phase → urgency → budget → action)
    ├── db.py           — SQLite layer (thread-local, multi-user, migrations)
    ├── dashboard.py    — HTTP dashboard + PWA + CRUD API (localhost:7734 / $PORT)
    ├── api.py          — FastAPI REST layer (localhost:7735) + cloud entrypoint
    ├── analyzer.py     — Signal analysis + NL commitment parsing (Claude API)
    ├── webhooks.py     — Slack/Discord outbound nudges + slash command handler
    ├── push.py         — Web Push delivery (VAPID + pywebpush)
    ├── pwa.py          — Manifest, service worker, icons
    ├── notify.py       — Shared notification delivery (push → webhooks → desktop)
    ├── features.py     — V3 feature flag registry (7 toggleable features)
    ├── logger.py       — Rotating structured log at ~/.vigil/vigil.log
    └── integrations/
        ├── __init__.py              — Fan-out runner (importlib-based, fault-isolated)
        ├── idle_detection.py        — Live: session heartbeat + idle escalation
        ├── adaptive_policy.py       — Live: per-category follow-through learning
        ├── reliability_reports.py   — Live: weekly Monday digest by category
        ├── github_integration.py    — Stub: stale PR commitment creation
        ├── calendar_sync.py         — Stub: pre-meeting commitment surfacing
        ├── accountability_partners.py — Stub: Slack DM to accountability partner
        └── shared_commitments.py    — Stub: team-visible commitments via Slack

~/.vigil/
    ├── commitments.db  — All data (commitments, decision_log, settings)
    └── vigil.log       — Structured log (grep-friendly)
```

---

## Tools

| Tool | What it does |
|---|---|
| `create_from_text` | Parse natural language into a commitment automatically |
| `save_commitment` | Capture a commitment with explicit fields (due date, intent, importance, recurrence) |
| `get_commitments` | List all pending with phase, state, and stats |
| `complete_commitment` | Mark done; auto-reschedules if recurring |
| `snooze_commitment` | Push deadline to a later time |
| `update_commitment` | Edit description, deadline, importance, intent, or summary |
| `explain_nudge` | Full decision trace for a commitment |
| `set_attention_budget` | Set daily nudge cap (default: 10) |
| `set_daily_digest_time` | Morning summary notification at HH:MM |
| `set_slack_webhook` | Route nudges to a Slack channel |
| `set_discord_webhook` | Route nudges to a Discord channel |
| `list_features` | Show all V3 features and their on/off state |
| `enable_feature` | Turn on a V3 feature by name |
| `disable_feature` | Turn off a V3 feature by name |

## Resources

| Resource | Content |
|---|---|
| `vigil://pending` | Pending commitments grouped by phase — Claude reads this at session start |
| `vigil://status` | System health: budget used, config, stats |
| `vigil://history` | Completed commitments and follow-through rate |

---

## Commitment schema

```python
{
  description:      str,
  due_by:           ISO 8601 datetime,
  ai_summary:       str,    # 2-sentence context of what was being worked on
  intent_strength:  "hard" | "soft" | "hypothetical",
  importance_score: 0.0–1.0,
  confidence_score: 0.0–1.0,
  state:            "captured" | "scheduled" | "in_progress" | "at_risk" | "overdue" | "completed" | "abandoned",
  recurrence:       "none" | "daily" | "weekly" | "monthly",
  recurrence_days:  ["monday", "thursday"],  # for weekly
  recurrence_time:  "HH:MM",
}
```

---

## V3 Features

Vigil V3 adds three directions of capability behind toggleable feature flags. All are off by default — turn them on via Claude or the dashboard.

### Enable via Claude

```
list_features()
enable_feature("idle_detection")
enable_feature("adaptive_policy")
enable_feature("reliability_reports")
```

### Enable at startup (env var)

```bash
VIGIL_FEATURES=idle_detection,adaptive_policy,reliability_reports vigil
```

### Feature registry

| Feature | Direction | Status | Required env var |
|---|---|---|---|
| `idle_detection` | Context Awareness | **Live** | — |
| `adaptive_policy` | Adaptive | **Live** | — |
| `reliability_reports` | Adaptive | **Live** | — |
| `github_integration` | Context Awareness | Stub | `GITHUB_TOKEN` |
| `calendar_sync` | Context Awareness | Stub | `GOOGLE_CALENDAR_TOKEN` |
| `accountability_partners` | Multi-Person / Team | Stub | `VIGIL_SLACK_BOT_TOKEN` |
| `shared_commitments` | Multi-Person / Team | Stub | `VIGIL_SLACK_BOT_TOKEN` |

### Idle Detection

Tracks the last time Claude read `vigil://pending` (i.e. the last time you opened a session). If no session is detected for 4+ hours and you have overdue or at-risk commitments, Vigil fires a push/Slack/desktop nudge.

- Cooldown: 2 hours between idle nudges
- Threshold: `VIGIL_IDLE_HOURS` env var (default: `4`)
- Resumes normal nudge schedule once you open a new session

### Adaptive Policy

Runs once per day. Looks at your completed and abandoned commitments grouped by `context` category. Categories with ≥ 3 resolved commitments and < 50% follow-through are marked low-reliability.

Pending commitments in those categories get their `importance_score` boosted 15% (capped at 0.95) so they are more likely to clear the policy engine's threshold and generate a nudge. Every boost is written to `decision_log` — `explain_nudge` shows you exactly why.

### Reliability Reports

Every Monday at 09:00 (configurable via `VIGIL_REPORT_HOUR`), Vigil sends a summary of the past week's follow-through, broken down by category:

```
Past 7 days: 7/10 done (70%)
3 still pending
By category:
  ✓ work: 4/5 (80%)
  ✓ personal: 2/3 (67%)
  ⚠ learning: 1/3 (33%)
```

Categories marked ⚠ are candidates for Adaptive Policy boosting. Report is deduped by ISO week — it won't re-fire if the process restarts.

---

## Web dashboard

A local dashboard runs at `http://localhost:7734` whenever Vigil is active.

- **Natural language bar** at the top — type "remind me to push the PR before EOD" and press Enter
- Stats: follow-through %, completed, pending, overdue, nudges today
- Commitments grouped by phase with colour-coded left borders
- **"Why?"** expandable button — full policy decision trace per commitment
- **Integrations panel** — paste a Slack or Discord webhook URL and save
- **V3 Features panel** — toggle switches for all 7 features, amber warning when env var is missing
- Auto-refreshes every 30 seconds
- Installable as a PWA (mobile)

---

## Installation

**Requirements:** Python 3.10+, macOS / Windows / Linux

```bash
git clone https://github.com/prodbysilky/vigil-mcp
cd vigil-mcp
python3 -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
```

### Option A — Claude Desktop (MCP) — simplest

```bash
pip install -e .
```

Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):

```json
{
  "mcpServers": {
    "vigil": {
      "command": "/absolute/path/to/vigil-mcp/.venv/bin/vigil-mcp"
    }
  }
}
```

Or with `uvx` (no venv needed):

```json
{
  "mcpServers": {
    "vigil": {
      "command": "uvx",
      "args": ["vigil-mcp"]
    }
  }
}
```

Restart Claude Desktop (Cmd+Q, then reopen). Vigil starts automatically.

### Option B — Standalone / ChatGPT / any HTTP client

```bash
pip install -e ".[api]"   # adds FastAPI + uvicorn
vigil                     # starts dashboard + REST API, no Claude needed
```

The REST API runs at `http://localhost:7735`. Interactive docs at `/docs`.
The dashboard runs at `http://localhost:7734`.

### Option C — Hosted mode (public URL, mobile push, auth-protected)

Deploy once, visit from any device, install the PWA to your home screen, get push notifications on your phone. Works on iOS 16.4+ and all Android.

**1. Generate VAPID keys** (one-time):

```bash
pip install -e ".[hosted]"
python -c "from vigil_mcp.push import generate_vapid_keys; \
           priv, pub = generate_vapid_keys(); \
           print('VIGIL_VAPID_PRIVATE_KEY=' + repr(priv)); \
           print('VIGIL_VAPID_PUBLIC_KEY=' + pub)"
```

**2. Deploy to Railway** (or Render / Fly — all read `$PORT`):

- New project → Deploy from GitHub repo
- Set environment variables (see table below)
- Railway auto-detects `Procfile` + `railway.json`; healthcheck hits `/manifest.json`

**3. Set a secret token** (so only you can access the dashboard):

```
VIGIL_SECRET_TOKEN=your-secret-here
```

Your dashboard URL becomes: `https://your-app.up.railway.app/?token=your-secret-here`

**4. Install on phone:**

- Open `https://your-app.up.railway.app/?token=your-secret-here` in Safari (iOS) or Chrome (Android)
- "Add to Home Screen"
- Launch from home screen, tap "Enable notifications"

---

## Environment variables

| Variable | Required | Purpose |
|---|---|---|
| `VIGIL_SECRET_TOKEN` | Recommended for hosted | Bearer token for dashboard + API auth |
| `ANTHROPIC_API_KEY` | For NL parsing + signal analysis | Powers `create_from_text` and auto-commitment creation |
| `VIGIL_VAPID_PRIVATE_KEY` | For Web Push | Signs push notifications |
| `VIGIL_VAPID_PUBLIC_KEY` | For Web Push | Sent to browsers for subscription |
| `VIGIL_VAPID_SUBJECT` | For Web Push | `mailto:you@example.com` (defaults to `mailto:admin@example.com`) |
| `VIGIL_SLACK_WEBHOOK` | For Slack nudges | Incoming webhook URL (alternative to setting via Claude/dashboard) |
| `VIGIL_DISCORD_WEBHOOK` | For Discord nudges | Webhook URL (alternative to setting via Claude/dashboard) |
| `VIGIL_SLACK_SIGNING_SECRET` | For Slack slash commands | Verifies `/vigil` commands come from Slack |
| `VIGIL_FEATURES` | Optional | Comma-separated list of V3 features to auto-enable on startup |
| `VIGIL_IDLE_HOURS` | Optional | Hours of inactivity before idle nudge fires (default: `4`) |
| `VIGIL_REPORT_HOUR` | Optional | Hour (0–23) for Monday reliability report (default: `9`) |
| `TIMEZONE` | Optional | Local timezone for recurring scheduling (default: `Europe/Budapest`) |
| `PORT` | Set by Railway/Render | Cloud port — auto-detected, do not set manually |
| `GITHUB_TOKEN` | For github_integration feature | Personal access token with `repo` scope |
| `GOOGLE_CALENDAR_TOKEN` | For calendar_sync feature | OAuth token for Google Calendar API |
| `VIGIL_SLACK_BOT_TOKEN` | For accountability/shared features | Bot token with `chat:write` scope |

---

## Slack integration

Vigil sends nudges to Slack and accepts `/vigil` slash commands.

### Outbound nudges (via webhook)

1. Go to [api.slack.com/apps](https://api.slack.com/apps) → Create New App → From Scratch
2. **Incoming Webhooks** → Enable → Add to Workspace → pick a channel → copy the URL
3. Set in Claude: `set_slack_webhook("https://hooks.slack.com/services/...")`  
   Or paste it in the dashboard Integrations panel.

### Inbound slash commands (`/vigil remind me to X`)

1. **Slash Commands** → Create New Command:
   - Command: `/vigil`
   - Request URL: `https://your-app.up.railway.app/api/slack/command`
   - Short Description: `Create a Vigil commitment`
2. **Basic Information** → Signing Secret → copy it → set `VIGIL_SLACK_SIGNING_SECRET` env var

Usage: `/vigil remind me to push the PR before EOD`

---

## Discord integration

1. In Discord: **Channel Settings** → **Integrations** → **Webhooks** → **New Webhook** → copy URL
2. Set in Claude: `set_discord_webhook("https://discord.com/api/webhooks/...")`  
   Or paste it in the dashboard Integrations panel.

Vigil sends rich embeds (amber colour) to the channel whenever a nudge fires.

---

## Example flows

**Natural language capture**
```
You:   "remind me to push this PR before EOD"
Vigil: parses → description="Push PR", due_by="today 18:00", importance=0.7
       → saved. No form filling needed.
```

**High-importance missed deadline**
```
You:     "I'll push this to GitHub before end of day"
Vigil:   saves commitment, importance=0.7, due=17:00

[17:00 — no action yet]
Policy:  phase=overdue, urgency=0.72, budget=3/10
         → NOTIFY: desktop + Slack: "You said you'd push to GitHub"

[next Claude session]
Claude:  "Hey — your GitHub push is overdue. Want to do it now?"
```

**Idle detection escalation**
```
You:     "I'll review those PRs this afternoon"
Vigil:   saves commitment, due=17:00

[No Claude session opened since 13:00 — 4 hours idle]
Idle:    phase=overdue, no session detected
         → PUSH + Slack: "4h idle — PR review is overdue"
```

**Adaptive policy in action**
```
[End of day — adaptive_policy runs]
Policy:  "learning" category: 1/4 resolved (25% rate)
         → 3 pending "learning" commitments boosted +15% importance
         → Next policy cycle: importance 0.5 → 0.58, clears threshold
         → Nudge fires that would have been suppressed
```

**Recurring commitment**
```
You:     "Every Monday morning I want to review my job applications"
Vigil:   saves with recurrence=weekly, recurrence_days=["monday"], recurrence_time="09:00"

[Monday 09:00] → NOTIFY
You:     "Done" → complete_commitment → next Monday auto-scheduled
```

**Weekly reliability report**
```
[Monday 09:00]
Vigil:  "Past 7 days: 7/10 done (70%)
         ✓ work: 4/5 (80%)
         ✓ personal: 2/3 (67%)
         ⚠ learning: 1/3 (33%)"
```

---

## REST API

The REST API runs at `http://localhost:7735` when `fastapi` + `uvicorn` are installed (`pip install vigil-mcp[api]`). Interactive OpenAPI docs at `/docs`.

| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/commitments` | List all pending |
| `POST` | `/commitments` | Save a commitment (structured) |
| `POST` | `/commitments/from-text` | Create from natural language |
| `GET` | `/commitments/{id}` | Get one commitment |
| `PATCH` | `/commitments/{id}` | Edit fields |
| `POST` | `/commitments/{id}/complete` | Mark done |
| `POST` | `/commitments/{id}/snooze` | Snooze to new time |
| `DELETE` | `/commitments/{id}` | Archive |
| `GET` | `/commitments/{id}/decisions` | Policy decision trace |
| `GET` | `/signals` | Recent behavioral signals |
| `POST` | `/signals` | Ingest a signal |
| `GET` | `/stats` | Overall follow-through stats |
| `GET` | `/settings` | Current settings |
| `PUT` | `/settings/budget` | Set daily attention budget |
| `PUT` | `/settings/digest` | Set daily digest time |
| `GET` | `/features` | List V3 features |
| `PUT` | `/features/{name}` | Enable/disable a V3 feature |

All endpoints accept `Authorization: Bearer <VIGIL_SECRET_TOKEN>` when a token is set.

---

## Logs

```bash
tail -f ~/.vigil/vigil.log
grep "action=notify" ~/.vigil/vigil.log
grep "auto_abandoned" ~/.vigil/vigil.log
grep "commitment_id=3" ~/.vigil/vigil.log
grep "adaptive_boost" ~/.vigil/vigil.log
grep "idle_nudge_sent" ~/.vigil/vigil.log
grep "reliability_report_sent" ~/.vigil/vigil.log
```

---

## Compatibility

| Platform | How to connect | Notes |
|---|---|---|
| **Claude Desktop** | `pip install -e .` → MCP config | Plug-and-play, full tool support |
| **Cursor / Cline / Zed** | Same as Claude Desktop | Works out of the box |
| **ChatGPT Custom GPT** | Deploy hosted mode → point GPT Action at `/openapi.json` | Full CRUD via REST |
| **iPhone / Android** | Visit hosted URL → Add to Home Screen → enable notifications | PWA + Web Push, no app store |
| **Slack** | Incoming webhook (outbound) + slash command (inbound) | `/vigil` creates commitments |
| **Discord** | Incoming webhook | Nudges as rich embeds |
| **Any web app / script** | REST API at `localhost:7735` or hosted URL | Standard JSON HTTP |
| **No AI at all** | `vigil` → open `localhost:7734` | Full dashboard UI — add/complete/snooze without any AI |

---

## Why this is different

- **Not a reminder app** — it reasons about *when* to interrupt you, not just *what* to remind you about
- **Not a wrapper** — the policy engine is the product
- **Explainable** — every decision is logged and queryable. Ask "why did you message me?" and get the full trace
- **Self-tuning** — adaptive policy adjusts importance scores based on your actual follow-through behavior
- **Idle-aware** — knows when you've been away too long and escalates accordingly
- **Local-first** — all data stays on your machine (SQLite in `~/.vigil/`)
- **Natural input** — type plain English; Vigil figures out the structure
- **Extensible** — V3 feature flag system lets you add GitHub, calendar, and team features when you need them

---

## Built with

- [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) — FastMCP server
- [Anthropic SDK](https://github.com/anthropics/anthropic-sdk-python) — NL parsing via claude-haiku
- SQLite — local-first, thread-safe persistence
- Python stdlib only for webhooks — no extra dependencies for Slack/Discord delivery
