Cadence — OpenClaw Skill¶
What this is¶
cadence is the user's personal productivity database, exposed as an MCP server. It is the single source of truth for:
- Tasks (Franklin Covey priority A1-C9, with rollover, spaces, goals, recurrence)
- Habits and habit logs
- Journal entries (with mood 1-5 and full-text search)
- Workouts (templates, logs, per-exercise sets/reps/weight/type)
- Focus sessions (Pomodoro-style, optionally linked to a task)
- Goals (with progress, categories, target dates)
- Spaces (projects/areas that group tasks/goals/habits)
- Weekly reviews, daily briefings, and insight cache (read + write)
The server runs on the user's VPS behind Tailscale. Reads and writes go straight to Postgres. You do not store anything locally — always go through the MCP tools.
Connection¶
The MCP server is already registered in the user's OpenClaw config as cadence. You do not need to configure auth — it's handled by the gateway (bearer token in the OpenClaw config). If tool calls fail with 401 / connection errors, stop and tell the user — do not fall back to local files or markdown templates.
Core rules¶
- Always use MCP tools. Never invent data, never fall back to scratch notes or file-based tracking. If a tool doesn't exist for what the user wants, say so.
- Today's date is authoritative. When the user says "today," use the current date (YYYY-MM-DD). The server also defaults most date fields to today if omitted.
- Tasks roll over.
list_taskswith no date returns today's tasks plus incomplete tasks from previous days. You don't need to manually pull overdue tasks. - Priority format is
A1-C9. Letter (A critical / B important / C nice-to-have) + digit 1-9 for sub-ordering. If the user just says "high priority" → useA1. If they say "low" →C1. Default if unspecified →B1(the tool handles this — just omit). - Dates are always
YYYY-MM-DD. Parse natural language ("tomorrow", "next Monday") into that format before calling tools. - Writes are authoritative. Don't ask "should I save this?" — if the user said "log my workout" / "add a task" / "journal this," just do it.
Tools¶
Most write tools that modify an existing row accept an optional
expected_updated_at(the ISO timestamp from your last read). Pass it for conflict-safe updates; omit it for last-write-wins, which is fine for most agent flows.
Tasks¶
list_tasks(date?, space_id?)— today's + overdue if no date; else that specific datecreate_task(title, notes?, priority?, task_date?, space_id?, goal_id?)— priorityA1-C9, defaults to B1update_task(task_id, title?, notes?, priority?, task_date?, done?)complete_task(task_id)delete_task(task_id)
Habits¶
list_habits(include_archived?)get_habit_stats(habit_id, days?)— default 30 dayscreate_habit(name, description?, frequency?, target_days?, color?)— frequencydaily|weekly; target_days ISO 1-7 (Mon=1, Sun=7)toggle_habit(habit_id, date?)— idempotent toggle; defaults to todayupdate_habit(habit_id, name?, description?, frequency?, target_days?, color?, archived?)—archived: truehides it,falserestoresdelete_habit(habit_id)— permanent; also deletes its logs. Preferupdate_habit(archived: true)to keep history
Journal¶
get_journal_entries(date?|from?,to?, limit?)— specific day or a rangesearch_journal(query)— full-text search over entry contentcreate_journal_entry(content, entry_date?, mood?)— mood 1-5; upserts if entry for that date existsdelete_journal_entry(entry_date)— delete that day's entry
Workouts¶
list_workout_logs(date?|from?,to?)— most recent 20 if no filterlist_workout_templates()log_workout(name, log_date, template_id?, duration_minutes?, notes?, exercises?)—exercisesis a JSON string array; when onlytemplate_idis supplied, its exercises are copied into a stable log snapshotcreate_workout_template(name, description?, exercises?)— save a reusable routine;exercisesis a JSON string array, each entry{name, type?, default_sets?, default_reps?, default_weight?, default_duration_seconds?, notes?}update_workout_template(template_id, name?, description?, exercises?)—exercisesreplaces the complete template list; omit it to preserve exercisesupdate_workout_log(log_id, name?, log_date?, duration_minutes?, notes?, exercises?)— only fields passed change;exercisesreplaces the list ([]clears), omit to leave it untoucheddelete_workout_log(log_id)— permanent; also removes its logged exercisesdelete_workout_template(template_id)— permanent; past logs created from it are kept
Focus¶
get_focus_sessions(from?, to?)get_focus_stats()— today's totalsstart_focus_session(duration_minutes, task_id?, break_minutes?)— duration 1-480, break 0-120complete_focus_session(session_id)pause_focus_session(session_id)— sets status to pausedresume_focus_session(session_id)— sets a paused session back to activeupdate_focus_session(session_id, task_id?, duration_minutes?, break_minutes?, started_at?, completed_at?, status?, notes?, expected_updated_at?)— correct history; null task unlinks itcancel_focus_session(session_id, expected_updated_at?)— preserve the session as cancelleddelete_focus_session(session_id, expected_updated_at?)— permanently remove a session
Goals¶
list_goals(status?)— statusactive|completed|abandonedcreate_goal(title, description?, category?, target_date?)— categoryhealth|career|personal|financial|learning|relationships|otherupdate_goal(goal_id, title?, description?, status?, progress?)— progress 0-100log_goal_progress(goal_id, progress)— just the progress percentagedelete_goal(goal_id)— permanent; also deletes progress logs. Preferupdate_goal(status: "abandoned")to keep the record
Spaces (projects)¶
list_spaces()create_space(name, description?)update_space(space_id, name?, description?, status?)— statusactive|paused|completeddelete_space(space_id)— permanent; linked tasks are kept but unlinked
Weekly Reviews¶
get_weekly_review(week_start?)— latest if omittedsave_weekly_review(week_start, content)— markdown
Daily Briefings¶
get_daily_briefing()— today's, if savedsave_daily_briefing(briefing_date?, content)— markdown; defaults to today
Insights (AI-generated insight cards)¶
get_insights()— today's cached insightssave_insights(cache_date?, insights)—insightsmust be an array or object
Calendar / summary¶
get_day_summary(date)— tasks + habits + journal + workouts + focus for that dayget_week_summary(week_start)— aggregate across a week
Prompt templates (via MCP prompts)¶
The server exposes prompt templates you can load for structured outputs. Use these when the user asks for something generative:
daily_planning— plan todaymorning_briefing— the daily 7am-style briefingend_of_day_review— end-of-day reflectionweekly_review— the weekly review structureweekly_trends— trend analysis over a weekproductivity_report— stats + narrativehabit_analysis— habit consistency deep-divegoal_check_in— per-goal progress check-ingoal_planning— set up a new goalspace_planning— plan a project/spaceweek_planning— plan out the upcoming weekjournal_prompt— journaling starter for todayworkout_suggestion— suggest a workout based on recent logs
Pattern: load the prompt → fill it with fresh data (call the read tools) → generate → save the result with the matching save tool (e.g. save_daily_briefing, save_weekly_review, save_insights).
What a loaded prompt looks like¶
When you call a prompt, the server runs the read queries itself and returns a messages array of {role: "user", content: {type: "text", text: "..."}} entries. You don't need to call list_tasks / list_habits beforehand — the prompt has already pulled fresh data and embedded it as JSON inside the text. Just generate a response to the returned messages, then call the matching save tool.
Example — loading morning_briefing returns a single user message whose text looks roughly like:
Good morning! Give me a quick briefing for 2026-04-20.
## What's on My Plate
- 7 tasks pending (2 are A-priority)
- 4 habits to track today
- 3 active goals in progress
### Task Details
[{"id":"...","title":"Ship MCP prompt docs","priority":"A1","done":false,"task_date":"2026-04-20"}, ...]
### Habits
[{"id":"...","name":"Morning walk","completed_today":false}, ...]
Keep it brief: 3-4 bullet points on what matters most today, then a one-sentence motivational close.
So the standard flow is: fetch morning_briefing with the MCP prompts_get utility → generate the briefing text → save_daily_briefing({content: "..."}) → deliver. If your MCP host does not expose prompt utilities, call Cadence's load_prompt({name: "morning_briefing"}) tool instead. Prompts that take args (productivity_report, weekly_review, goal_planning, space_planning) accept them in load_prompt.arguments, for example load_prompt({name: "goal_planning", arguments: {goal_id: "..."}}).
Prompts with args:
- productivity_report(from, to) — both YYYY-MM-DD, required
- weekly_review(week_start?) — defaults to this week's Monday
- goal_planning(goal_id) — resolve the ID via list_goals first
- space_planning(space_id?) — omit for cross-space planning
All other prompts take no args.
Resources (read-only URIs)¶
For quick contextual reads you can fetch resources instead of calling tools:
cadence://dashboard— today at a glancecadence://tasks/today,cadence://tasks/overduecadence://habits/today,cadence://habits/streakscadence://journal/today,cadence://journal/recentcadence://workouts/recentcadence://focus/todaycadence://goals/activecadence://spaces/listcadence://briefing/todaycadence://calendar/today,cadence://calendar/weekcadence://review/latest
Common patterns¶
"What's on my list today?"
→ list_tasks with no args. Show them, grouped by priority letter.
"Add a task: X"
→ create_task({title: "X"}). Don't ask for priority — the default B1 is fine unless the user specifies.
"Mark X done" / "I finished X"
→ list_tasks → find by title → complete_task(task_id).
"Log my workout"
→ Ask for what was done if not stated, then log_workout with the exercises array. Include type on each exercise (strength/timed/cardio).
"How am I doing on habits?"
→ list_habits → for each, get_habit_stats. Summarize completion rates.
"Journal for today: ..."
→ create_journal_entry({content: "..."}). If they also mention a mood number, include it.
"Start a 25 minute focus session on X"
→ list_tasks → find X → start_focus_session({duration_minutes: 25, task_id}).
Morning briefing (7am cron)
→ Load morning_briefing prompt → pull dashboard resource → generate → save_daily_briefing → deliver to Telegram.
Weekly review (Sunday evening cron)
→ Load weekly_review prompt → pull calendar/week + get_week_summary → generate → save_weekly_review.
What NOT to do¶
- Do not create parallel tracking in markdown files, local notes, or templates. The MCP server is the only store.
- Do not skip the save step after generating a briefing or review — always persist it via the save tool so the dashboard shows it.
- Do not retry failed writes indefinitely. If a tool returns an error, surface it to the user with the error text.
- Do not guess IDs. Always resolve a task/habit/goal by listing first, then act on the returned
id.