lockin-mcp
v1.0.14
Published
LockIn MCP — block distractions at the system level with AI-powered focus tools
Maintainers
Readme
LockIn MCP
A Model Context Protocol (MCP) server that blocks distracting websites at the system level by managing entries in /etc/hosts. Use it with Poke, Claude Desktop, or ChatGPT custom MCP connectors to stay focused.
Features
| Tool | Description |
|------|-------------|
| get_agent_guidelines | Coaching instructions for AI agents (when to enforce focus, how to handle unblock requests) |
| block_domains | Block a list of domains (e.g. youtube.com, x.com, tiktok.com) |
| unblock_domains | Permanently unblock specific domains |
| temporarily_unblock_domains | Allow access for a limited time, then auto re-block |
| enter_focus_mode | Block common distraction sites in one action (YouTube, X, TikTok, Instagram, Reddit, Facebook, Netflix, Twitch) |
| get_block_status | Show what is blocked, temporarily allowed, and system readiness |
Blocking redirects domains to 127.0.0.1 and ::1, including www. variants.
Pro license required: block_domains, unblock_domains, temporarily_unblock_domains, and enter_focus_mode require an active Pro license. get_agent_guidelines and get_block_status are read-only and work without Pro. For local development only, set MDB_SKIP_LICENSE_CHECK=1.
Requirements
- macOS / Linux / Windows (hosts-based blocking; macOS uses
dscacheutilfor DNS flush) - Node.js 18+
- Administrator privileges to modify
/etc/hosts(see below)
Web App & Pro Licensing
The web/ directory contains the landing page, user accounts, Stripe checkout, and license API.
Quick start (local)
# Terminal 1 — web app
cd web
cp ../.env.example .env.local # edit with your Stripe keys
npm install
npm run dev # http://localhost:3001
# Terminal 2 — CLI installer (points at local API by default)
export MDB_APP_URL=http://localhost:3001
export MDB_LICENSE_API_URL=http://localhost:3001/api/v1
npm run build
npm run install:local
# or from GitHub (no clone):
# npx -y lockin-mcp installArchitecture
| Component | Purpose |
|-----------|---------|
| Landing page (/) | Features, how-it-works, pricing |
| Device auth (/device) | Browser login for CLI OAuth device flow |
| Dashboard (/dashboard) | Pro status, license key, MCP connection card with live blocked sites |
| Dashboard sessions (/dashboard/sessions) | Focus time history and Chrome extension distraction journey |
| Dashboard focus stats (/dashboard/focus) | Heatmap, streaks, and weekly share image |
| Dashboard agents (/dashboard/agents) | Connect OAuth agents (Poke, Claude web, ChatGPT, Cursor, Hermes, OpenClaw); revoke connections |
| Chrome extension (/extension/connect) | OAuth sign-in for Pro users; logs blocked-site visits during active focus |
| Report (/report) | Bug reports and feature requests (rate-limited; not indexed) |
| MCP OAuth consent (/oauth/consent) | Browser approval for ChatGPT / Claude web MCP connectors |
| Checkout (/checkout) | Stripe checkout (requires verified email) |
| API (/api/v1/*) | License verify, OAuth device flow, token status |
| Stripe webhook | Pro upgrade + license key generation |
CLI auth handshake
- Installer calls
POST /api/v1/oauth/device→ receivesuser_code+device_code - Browser opens
/device?code=ABCD-1234 - User signs in and approves (requires Pro)
- Installer polls
POST /api/v1/oauth/token→ receivesaccess_token - Daemon verifies token via
POST /api/v1/license/statuson startup and hourly
MCP OAuth (ChatGPT, Claude web)
- MCP client registers via DCR at
POST /api/v1/oauth/register(proxied from relay atPOST /register) - Client discovers metadata from
/.well-known/oauth-authorization-server(web app and relay) - User authorizes at
/api/v1/oauth/authorize→ browser consent at/oauth/consent - Client exchanges code at
POST /api/v1/oauth/token - MCP requests to relay use OAuth access tokens or the device
mdb_*API key
Environment variables
See .env.example for all configuration options.
Production deployment
Use this checklist before pointing lockinmcp.com at live traffic.
1. Generate secrets (once)
openssl rand -base64 32 # MDB_JWT_SECRET
openssl rand -base64 32 # MDB_SERVER_SECRET
openssl rand -base64 32 # REGISTRATION_SECRET (relay)Set Vercel and Convex production env vars separately in each dashboard — do not copy MDB_SERVER_SECRET from your laptop with a sync script (local dev secrets are different from production).
| Secret | Vercel | Convex prod |
|--------|--------|-------------|
| MDB_SERVER_SECRET | Yes — same value on both | Yes — npx convex env set MDB_SERVER_SECRET '…' --prod once |
| MDB_JWT_SECRET | Yes — same value on both (OAuth state signing) | Yes — npx convex env set MDB_JWT_SECRET '…' --prod once |
| LOCKIN_CHROME_EXTENSION_IDS | Yes — published Chrome Web Store extension ID(s), comma-separated | No |
| STRIPE_* | Yes | No |
2. Convex production
cd web
npx convex deploy # production only — not convex devSet env vars in the Convex dashboard → Production → Settings → Environment Variables. Do not run ./scripts/sync-convex-env.sh for production — it only targets your dev deployment and .env.local often has localhost URLs.
| Convex prod variable | Value |
|---------------------|--------|
| MDB_APP_URL | https://www.lockinmcp.com |
| MDB_SERVER_SECRET | Same string as Vercel |
| MDB_JWT_SECRET | Production JWT secret (Convex only) |
| GOOGLE_* / GITHUB_* | Production OAuth app credentials |
| RESEND_API_KEY / RESEND_FROM_EMAIL | Production Resend |
| MDB_RELAY_URL / MDB_RELAY_PUBLIC_URL | https://relay.lockinmcp.com |
| REGISTRATION_SECRET | Same as relay Worker |
One-off CLI (paste production values, not from .env.local):
cd web && npx convex env set MDB_APP_URL 'https://www.lockinmcp.com' --prod3. Stripe live mode
In Stripe Dashboard (toggle Live):
| Variable | Where to get it |
|----------|-----------------|
| STRIPE_SECRET_KEY | Developers → API keys → sk_live_... |
| STRIPE_PRICE_ID | Products → one-time $19.99 lifetime price → price_... |
| STRIPE_PRICE_ID_TIERED | Products → one-time $9.99 lifetime price (tiered batch 1) → price_... |
| STRIPE_WEBHOOK_SECRET | Developers → Webhooks → endpoint https://www.lockinmcp.com/api/stripe/webhook → whsec_... |
| STRIPE_PROMOTION_CODE_ID | Products → Coupons → LOCKIN50 (50% off) → promotion code promo_... (auto-applied on discounted checkout) |
Checkout appearance (product name, short description, logo, colors) is set in code via web/lib/stripe-checkout-display.ts when each session is created — you do not need a long product description in the Stripe Dashboard.
Webhook events: checkout.session.completed, checkout.session.created, checkout.session.expired (follow-up scheduling backup)
Abandoned checkout follow-up (24h email)
When a signed-in user starts Stripe Checkout but does not pay, a follow-up email is scheduled via Convex (ctx.scheduler.runAfter). After deploy:
cd web && npx convex deploy
./scripts/backfill-oauth-email-verified.sh --prod # one-time OAuth emailVerified backfillVerify production:
- Convex
MDB_APP_URLishttps://www.lockinmcp.com(notlocalhost). - Vercel
MDB_SERVER_SECRET=== ConvexMDB_SERVER_SECRET. - Stripe webhook enables
checkout.session.createdandcheckout.session.expired. - Preview email:
./scripts/send-abandoned-checkout-preview.sh [email protected] - Dev smoke test (60s delay):
npx convex env set CHECKOUT_FOLLOW_UP_DELAY_MS 60000then./scripts/test-abandoned-checkout-schedule.sh <userId> - Inspect jobs:
./scripts/list-checkout-follow-ups.sh scheduled
Optional Convex env: CHECKOUT_FOLLOW_UP_DELAY_MS (default 86400000 = 24h).
4. OAuth (production callbacks)
| Provider | Redirect URI |
|----------|--------------|
| Google | https://www.lockinmcp.com/api/auth/oauth/google/callback |
| GitHub | https://www.lockinmcp.com/api/auth/oauth/github/callback |
Add GOOGLE_* / GITHUB_* to Vercel and Convex production dashboards (not dev keys from .env.local).
5. App URLs (hosting + Convex)
MDB_APP_URL=https://www.lockinmcp.com
NEXT_PUBLIC_APP_URL=https://www.lockinmcp.com
MDB_LICENSE_API_URL=https://www.lockinmcp.com/api/v1
MDB_RELAY_PUBLIC_URL=https://relay.lockinmcp.com
MDB_RELAY_URL=https://relay.lockinmcp.comThe apex domain (lockinmcp.com) redirects to www in production.
6. Deploy web app
Deploy the web/ Next.js app to your host (Vercel, etc.) with all env vars from .env.example.
7. Smoke test
- Sign up → verify email → checkout with a real card (or Stripe test in test mode first)
- Confirm
/dashboardshows Pro + license key - Run installer:
curl -fsSL https://www.lockinmcp.com/install | bash - Approve device at
/device→ confirm MCP tunnel provisions
One-Line Installer
The fastest way to set up blocking, tunneling, and Poke integration:
macOS / Linux (curl)
curl -fsSL https://raw.githubusercontent.com/Kiog-Aser/LockIn/main/install.sh | bashOr use the hosted installer (recommended):
curl -fsSL https://www.lockinmcp.com/install | bashWindows (PowerShell)
iwr -useb https://raw.githubusercontent.com/Kiog-Aser/LockIn/main/install.ps1 | iexnpm
npx -y lockin-mcp install
# or from a local clone:
npm run install:localThe npm package lockin-mcp replaces the legacy mac-distraction-blocker-mcp name. Legacy bin aliases (mac-distraction-blocker-mcp, mdb-install) still work.
What the installer does
- Detects your platform (macOS, Linux, or Windows)
- Verifies your $9.99 Pro license via browser login or license key
- Prompts for distraction sites — popular presets pre-selected (YouTube, X, TikTok, Instagram, Reddit) plus custom domains
- Applies initial blocks to
/etc/hosts - Starts the MCP HTTP server locally with an outbound relay connection (background LaunchAgent on macOS)
- Provisions a stable relay URL (
https://relay.lockinmcp.com/device/{id}/mcp) so Poke, Claude, and ChatGPT can reach your Mac - Connects your AI agent — Poke, Claude (web), ChatGPT, Cursor, Hermes, and OpenClaw connect via the dashboard or OAuth; Claude Desktop uses
connect-agentCLI + mcp-remote (not on the dashboard grid) - Auto-updates the daemon — when a background service is installed, checks npm every 5 minutes and restarts after upgrading
Installer flags (non-interactive)
curl -fsSL .../install.sh | bash -s -- --yes --browser-login --poke-recipe
npx -y lockin-mcp install --license-key lockin_pro_xxx --sites all --poke-recipe| Flag | Description |
|------|-------------|
| --yes | Non-interactive with defaults |
| --license-key <key> | Pro license key |
| --browser-login | Verify license via browser device flow (default) |
| --skip-tunnel | Local MCP only (no relay URL) |
| --skip-block | Skip initial domain blocking |
| --skip-poke | Skip Poke URL generation |
| --port <number> | Local MCP HTTP port (default 3847) |
| --sites all | Block all presets |
| --custom-domains a.com,b.com | Extra domains |
| --poke-recipe | Generate distraction-coach recipe URL |
| --mcp-auth-token <token> | Custom bearer token for tunneled MCP |
Setup details are saved to ~/.lockin/setup-manifest.json.
Pro licensing ($9.99 lifetime — 50% off $19.99)
- Purchase: Create an account at
/login(email or Google/GitHub;/signupredirects here), verify email, pay via Stripe - License key: Available on dashboard after purchase; pass
--license-keyto installer - Browser login: Default installer flow — OAuth device authorization in your browser
- Auth: Google and GitHub OAuth supported; email/password requires verification before checkout
- Support: Bug reports and feature requests at
/report(also linked in the site footer)
Quick Start (manual)
git clone https://github.com/Kiog-Aser/LockIn
cd LockIn
npm install
npm run buildRun locally (stdio — advanced)
For MCP clients that spawn a local subprocess:
node dist/index.js --stdioMCP Client Configuration (after installer)
Your MCP URL and API key are in ~/.lockin/setup-manifest.json:
- MCP URL: from manifest — e.g.
https://relay.lockinmcp.com/device/abc123/mcp(Poke, Claude, ChatGPT, Cursor, Hermes, OpenClaw) - API key:
mdb_…
Dashboard connect (recommended)
After install, open lockinmcp.com/dashboard/agents to connect OAuth agents or copy CLI commands for Cursor, Hermes, and OpenClaw. Claude Desktop is not on the dashboard grid — use the CLI below.
connect-agent CLI
For Claude Desktop or guided setup from the terminal:
npx -y lockin-mcp connect-agent
npx -y lockin-mcp connect-agent --agent claude-desktopPoke
The installer copies your relay MCP URL and opens poke.com/integrations/new. In Poke:
- Paste the MCP URL (ends with
/mcp) - Choose OAuth authentication (do not paste your API key in the integration form)
- Sign in on lockinmcp.com when prompted
- Install the LockIn recipe
Legacy CLI (optional): npx poke@latest mcp add 'https://YOUR_RELAY_HOST/device/YOUR_ID/mcp' -n 'LockIn MCP' with OAuth in the Poke UI.
Claude (claude.ai)
The installer opens a link that pre-fills the connector name and MCP URL (connectorName + connectorUrl). Claude web uses OAuth — do not paste your API key in Advanced settings. See the connector setup guide.
- Choose Claude (claude.ai) in the installer (or build the link with
connectorName=LockIn MCPand your encoded MCP URL) - Confirm the pre-filled MCP server URL (e.g.
https://relay.lockinmcp.com/device/abc123/mcp) - Leave Advanced OAuth settings empty. Click Add, then sign in on lockinmcp.com when Claude prompts (Pro license required)
- Start a new chat with LockIn enabled
For Bearer/API-key auth, use Claude Desktop instead — the installer can write claude_desktop_config.json automatically.
Claude Desktop
Claude Desktop is not on the dashboard agent grid — connect via CLI:
npx -y lockin-mcp connect-agent --agent claude-desktopClaude Desktop only supports stdio MCP servers in claude_desktop_config.json — not direct url entries. The installer or connect-agent merges a config that uses mcp-remote to bridge your relay URL (requires Node.js 18+).
Config paths:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Manual example:
{
"mcpServers": {
"lockin-mcp": {
"command": "npx",
"args": [
"-y",
"mcp-remote@latest",
"https://relay.lockinmcp.com/device/YOUR_ID/mcp",
"--header",
"Authorization:${LOCKIN_MCP_AUTH_HEADER}"
],
"env": {
"LOCKIN_MCP_AUTH_HEADER": "Bearer mdb_..."
}
}
}
}On Windows, use "command": "npx.cmd" instead of "npx". Quit Claude Desktop completely before editing, then reopen.
Cursor, Hermes, OpenClaw
Connect from the dashboard Agents page — copy your relay MCP URL or run the CLI command shown for each provider. See connector guides for Hermes and OpenClaw setup.
ChatGPT
Custom MCP connectors require Developer mode under Advanced settings. ChatGPT cannot accept credentials from an external link — copy from the installer or setup guide.
- Open ChatGPT → Settings → Connectors → Advanced
- Enable Developer mode
- Add connector → paste MCP server URL from your manifest
- Set Authentication to OAuth and leave DCR enabled — sign in on lockinmcp.com when prompted (do not paste your API key in OAuth fields)
- Start a new chat and enable the LockIn connector
Relay deployment (operators)
The relay/ package is a Cloudflare Worker + Durable Object that bridges remote MCP HTTP requests to each Mac daemon over an outbound WebSocket.
cd relay
npm install
npx wrangler secret put REGISTRATION_SECRET
npx wrangler secret put MDB_JWT_SECRET # optional, same as web app
npm run deploySet relay vars on Convex production in the dashboard. For local dev only: web/.env.local + ./scripts/sync-convex-env.sh (dev deployment).
Local OAuth development
When running the web app locally (http://localhost:3001):
- MCP OAuth CORS allows
localhost:3000/3001and127.0.0.1:3000/3001 - Dynamic Client Registration redirect URIs must use
http://localhost:…(not127.0.0.1) per OAuth validation
Chrome extension (optional)
The Chrome extension tracks page visits during active focus sessions and syncs them to your dashboard distraction journey.
- Load unpacked from the
extension/folder (seeextension/README.md) - Click the extension icon → Sign in (OAuth at lockinmcp.com/extension/connect)
- Pro license required — only blocked-site visits during active focus are logged
- View the journey on Dashboard → Sessions (expand a day)
Daemon auto-update
When the background service (LaunchAgent/systemd/Windows task) is installed, the daemon checks npm for new lockin-mcp releases every 5 minutes (first check after 5 seconds). Updates install to ~/.lockin/npm/ and restart the service automatically. Last check timestamp: ~/.lockin/last-update-check.json.
Force an update check anytime:
npx lockin-mcp updatePrivileges & /etc/hosts
The server writes marked entries between:
# lockin-mcp BEGIN
...
# lockin-mcp ENDLegacy installs may still show # mac-distraction-blocker-mcp markers; those are stripped on the next hosts sync.
Only those lines are touched; the rest of /etc/hosts is preserved.
Option A: Run the server with sudo (simplest)
sudo node dist/index.js --http --port 3847For stdio MCP clients, configure the client to launch with sudo (see client config examples below).
Option B: Passwordless sudo for hosts updates (recommended)
The installer creates ~/.lockin/update-hosts.sh and a sudoers entry at /etc/sudoers.d/lockin-mcp-hosts that allows your user to run that script without a password. Temp files use the prefix lockin-mcp-hosts-{pid}.
If you installed manually, re-run the installer or configure sudoers to allow (replace YOUR_USER with your macOS username):
sudo visudo -f /etc/sudoers.d/lockin-mcp-hostsAdd:
YOUR_USER ALL=(ALL) NOPASSWD: /Users/YOUR_USER/.lockin/update-hosts.sh(Adjust paths for Linux/Windows as needed.)
Verify permissions
Call get_block_status — hostsFileWritable: true means the server can update blocking without extra setup.
Legacy: local stdio (Claude Desktop)
~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"lockin-mcp": {
"command": "node",
"args": ["/absolute/path/to/LockIn/dist/index.js", "--stdio"]
}
}
}Security
- MCP bearer auth: HTTP MCP requires
MDB_MCP_AUTH_TOKENor--mcp-auth-token(mdb_*tokens). Relay-backed HTTP refuses to start without a token. - API rate limiting: Login, signup, OAuth (account + MCP), license verify, checkout, consent, and user-report routes are rate-limited.
- User reports:
POST /api/reportaccepts bug/feature submissions (5 per IP per hour). RequiresRESEND_API_KEYin production; falls back to Convex logs in dev. - Relay tokens: Device tokens are hashed at rest on the Cloudflare worker (SHA-256).
- Setup manifest: Written with
0o600permissions; contains your relay URL and API key — do not commit or share.
Hooking up to poke.com/kitchen
The installer tunnels your local MCP over HTTPS so Poke can connect remotely — no extra terminal tab. When you choose Poke during install, it copies your relay URL and opens the integrations page for OAuth setup.
Recipe / Kitchen (recommended)
Install the official LockIn recipe for the best Poke experience:
https://poke.com/r/KQ7myvC_Xpo
The recipe connects your MCP tools and coaches you through focus sessions. You can also build custom recipes in poke.com/kitchen using get_agent_guidelines, block_domains, get_block_status, etc.
Tool Examples
Get agent guidelines
No arguments. Returns focus-coaching instructions for AI agents — call at the start of focus or blocking conversations.
Block sites
{
"domains": ["youtube.com", "x.com", "tiktok.com", "reddit.com"]
}Unblock one site
{
"domains": ["youtube.com"]
}15-minute break
{
"domains": ["youtube.com"],
"duration_seconds": 900
}Check status
No arguments. Returns blocked list, temporary allowances with expiry timestamps, and whether hosts is writable.
How it works
flowchart LR
A[MCP Client / Poke] --> R[Relay Worker]
R --> B[MCP Server on Mac]
B --> C[State JSON\n~/.lockin/]
B --> D[/etc/hosts]
B --> E[DNS cache flush]
C --> B- State — Block list and temporary unblocks live in
~/.lockin/state.json - Hosts sync — Active blocks are written to
/etc/hostswith a managed marker - Expiry watcher — A background timer re-applies blocks when temporary unblocks expire (server process must be running)
- DNS flush — Best-effort
dscacheutil/mDNSResponderrefresh after changes
Development
npm install
npm run dev # watch TypeScript
npm run build
npm run lint
npm test # Vitest (root MCP tests)
cd web && npm test # web rate-limit tests
cd relay && npm test # relay token hashing testsCI (.github/workflows/ci.yml) runs lint, test, build, and npm audit --audit-level=high for the MCP server, web app, and relay worker.
Reset local install state (development only):
npx lockin-mcp dev-reset # reset hosts, manifest, relay config
npx lockin-mcp dev-reset --full # also remove LaunchAgent / background service
npx lockin-mcp dev-reset --wipe-license # clear saved licenseSee relay/README.md for Cloudflare WebSocket hibernation details on the device relay worker.
Troubleshooting
| Issue | Fix |
|-------|-----|
| EACCES on block/unblock | Run with sudo or configure passwordless sudo (see above) |
| Site still loads | Hard-refresh browser; DNS cache may take a moment. Try private window. |
| Temp unblock didn't re-block | Keep the MCP server process running (expiry watcher runs in-process) |
| Poke can't connect | Re-run install and confirm OAuth completed in Poke. Check manifest relay URL + /health returns lockin-mcp with relay.connected: true. If offline, re-run npx lockin-mcp install once — the daemon self-heals reconnects automatically. |
| Wrong platform | macOS, Linux, and Windows are supported; DNS flush behavior differs by OS |
License
MIT
