get_goal
Get goal
The one yearly goal: kind (count or done), metric, starting value, current value, target, deadline, progress, levers (hard/comfortable) and other projects. Call first in most flows.
Takes no parameters.

io.github.YahhiPublished by GitHub account Yahhiv0.4.2Updated Nov 1, 2026
Accountability partner in your agent: one yearly aim, one main step a day, a timer your agent runs.
AimYear is a remote accountability server that lets an assistant hold one yearly goal, plan a daily main step, run a work timer and review where time went.
Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.
Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.
Read https://sourceweft.com/mcp/io.github.Yahhi%2Faimyear-mcp/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.Add this to your client's mcpServers config.
{
"mcpServers": {
"aimyear": {
"type": "http",
"url": "https://mcp.aimyear.com/mcp"
}
}
}An accountability partner inside the AI agent you already use. Connect AimYear to Claude, Claude Code, ChatGPT or any MCP client, and your agent keeps one big aim for your year, plans one main step a day toward it, runs the timer while you work and writes down what actually got done. When you start comfortable work while the main step is still open, it asks you to do the step first.
Built for indie developers and makers who build all day but keep postponing the work that moves the goal: selling, talking to users, publishing.
Website: aimyear.com · Setup guide: aimyear.com/connect.html · Privacy: aimyear.com/privacy.html · Contact: [email protected]
Endpoint: https://mcp.aimyear.com/mcp (Streamable HTTP, OAuth sign-in with Google,
Apple or email). Nothing to install.
claude mcp add --transport http aimyear https://mcp.aimyear.com/mcp, then
/mcp → aimyear → Authenticate.AimYear, URL the endpoint
above, authentication OAuth → Create, then sign in.Then say: "Help me set my aim for this year". The agent checks that the aim is countable, builds the path of stages, and from then on: "plan my day", "start the timer", "how was my week?".
The skill that teaches an agent the method is in skills/aim/SKILL.md.
The server stores what your agent saves through the tools (aim, path, plans, sessions and their summaries) in Google Cloud Firestore. Your conversation stays with your agent's provider; AimYear only receives the tool calls. Logs keep the tool name, success and timing, never argument values. To delete your account and data, email [email protected]. Details: privacy policy.
Names say what a tool does: get_ reads one thing, list_ reads several, find_ looks
for problems, set_ replaces a whole list, add_ / edit_ / delete_ change one entry.
0.4.2 adds the hard-first check: set_plan warns when nothing in the day is on a hard lever,
and add_steps / set_levers warn when a stage starts with preparation before the first
hard-lever step. The server instructions tell every agent the same rule.
0.4.0 adds routines: small steps that repeat on fixed weekdays ("talk with one person on
Reddit"). The server adds them to each matching day's plan, so every device and agent sees
the same checks; get_now lists the open ones and summarize_week counts them.
0.3.0 renamed most tools (0.2.x names stop working) and added the editing tools:
edit_session, delete_session for finished sessions, edit_milestone, delete_step,
edit_plan_item, delete_plan_item, set_projects, and get_settings / set_settings
with the timezone.
Stateless Streamable HTTP, POST only. Sign-in is OAuth 2.1 with PKCE and dynamic client registration. The sign-in page uses the same Firebase project as the AimYear app, so the app and every connected AI share one account and one history. Guest (anonymous) accounts cannot be connected. Every tool acts as one signed-in person; see docs/ACCESS.md.
Without a token, the server describes itself (initialize, tools/list, prompts/list,
ping); everything else answers 401 with a WWW-Authenticate header pointing at
/.well-known/oauth-protected-resource/mcp.
The server is the source of truth, and a phone may deliver a request long after the tap, or
twice. So stop_session, start_session, add_session, add_note, add_steps and
delete_session take an optional op_id: an id the app mints before its first attempt and
reuses on every retry. The write is applied once; a retry gets the first answer back and
writes nothing. Applied ops live in users/{uid}/ops/{op_id} (tool, sessionId, result,
at, expiresAt) and are written in the same Firestore transaction as the change they
record. Give the ops collection a TTL policy on expiresAt (30 days) in the Firestore console.
stop_session takes end_at and start_session takes start_at (ISO 8601) so a stop queued
offline at 14:05 and delivered at 16:40 records 14:05. start_session also takes the app's
own session_id (create-if-absent). A request with op_id, end_at or start_at must name
its session_id: the server never guesses which session a replay means. Without them, an
agent's stop_session / delete_session acts only on its own connection's single running
session, and otherwise lists the running sessions with ids.
Two devices can stop one session; the result is the same whatever order the stops arrive in:
the earliest end wins (once anyone stopped it, it was not running), done wins over any
other outcome, the first summary stays and a different later one goes to the notes. Every
stop is kept in the session's stops[], and the answer says applied: ended | shortened | recorded.
The rest of this file is for working on the server itself. You don't need it to use AimYear.
Data lives in Firestore under users/{uid}/aim/{goal,path,profile}, users/{uid}/days,
users/{uid}/sessions, users/{uid}/blocks, users/{uid}/ops (applied op_ids, TTL on
expiresAt); OAuth under oauth_clients, oauth_requests, oauth_codes, oauth_tokens;
reminders in pending_notifications (claimed by a transactional delete before each push).
Credentials: a Firebase admin service account at ~/.aimyear/service-account.json (or
AIMYEAR_SERVICE_ACCOUNT=<path>; falls back to Application Default Credentials).
Env: AIMYEAR_UID (stdio), AIMYEAR_TOKENS (HTTP: static token=uid pairs for Claude Code
scripts, the menu bar app and the scheduler), AIMYEAR_BASE_URL (the public URL; the OAuth
issuer), AIMYEAR_ALLOWED_ORIGINS (extra browser origins), AIMYEAR_WORKPLACE, AIMYEAR_TZ.
When you add a tool: give it an entry in src/access.ts (npm test fails without one), run
npm run access-doc, bump src/version.ts, run npm run surface-lock.
Env vars on the service: AIMYEAR_TOKENS, AIMYEAR_TZ, AIMYEAR_PROJECT,
AIMYEAR_BASE_URL=https://mcp.aimyear.com. mcp.aimyear.com is a Cloud Run domain mapping
(CNAME to ghs.googlehosted.com in Route 53) and is in Firebase Auth's authorized domains.
Health: /health. The legacy URL https://<service>/c/<token>/mcp still works.
Call analytics: scripts/mcp-calls.sh [1d] prints calls per tool and the call sequences
per person and client.
FSL-1.1-MIT: you can read, run, self-host and change this server, but not offer it as a competing commercial service. Each release becomes MIT two years after it is published. The hosted service is at https://mcp.aimyear.com/mcp.
Source: README.md at commit 99d96f4
Get goal
The one yearly goal: kind (count or done), metric, starting value, current value, target, deadline, progress, levers (hard/comfortable) and other projects. Call first in most flows.
Takes no parameters.
Check goal draft
Check a goal that is not saved yet against the rubric (a countable metric with value today and target, or a done-or-not result you can check yes or no; deadline within 12 months; pace per week). Changes nothing. Returns problems[] and next_question: loop until problems is empty, then call set_goal. Prefer count; use done only when no honest number exists.
kindtitlemetrictargetbaselinedeadlineSet goal
Save the yearly goal, or replace it with the next one. Rejects with problems[] if the rubric fails. Keeps levers and projects. area is which of the five life areas the goal belongs to.
whyareakindtitlerequiredmetrictargetbaselinedeadlinerequiredRecord progress
Record the current value of the goal metric ("I now have 4 customers"), or of a countable milestone when milestoneId is given ("I have talked to 7 people"). For a done-or-not goal, 1 marks it done. One value per date: calling again for the same date corrects it. Says when the target is reached; a countable milestone that reaches its target is done.
datevaluerequiredmilestoneIdSet levers
Replace the list of levers: the recurring kinds of goal work. At least one must be kind=hard (the work the user avoids). keywords/repos are hints for classifying sessions from their task text. Pass existing ids to keep them.
leversrequiredSet projects
Replace the list of other projects (paid work, other products, life routines), so their sessions count as other work or life, not goal work. Pass the whole list: leave one out to remove it, change its name or hints to edit it. Pass existing ids to keep them.
projectsrequiredGet path
Milestones with kind, progress (recorded value or steps done), pace (ahead/on_pace/behind) and their steps. current_milestone_id is the one to work on.
Takes no parameters.
Set milestones
Replace the list of 2–6 ordered milestones under the goal, each countable (metric and target) or measured by its steps, with a due date before the goal deadline. Pass each existing id, or that milestone's steps are deleted. To change one milestone, use edit_milestone.
milestonesrequiredEdit milestone
Change one milestone: title, kind (count or steps), metric, target, due date or status. status=retired drops it without finishing; done or retired moves the path to the next milestone. Its steps are kept.
duekindtitlemetricstatustargetmilestoneIdrequiredAdd steps
Add concrete steps to a milestone. A step is an action that fits a few work blocks ("email 10 people from the waitlist"), not "do marketing". Abstract steps are rejected. Put a hard-lever step first; preparation only where it blocks it (warns otherwise).
op_idstepsrequiredmilestoneIdrequiredEdit step
Change one step: title, status (todo/doing/done), order, lever, estimate.
ordertitlestatusstepIdrequiredleverIdestimateMinDelete step
Delete one step from the path.
stepIdrequiredNext steps
Open steps of the current milestone, hard-lever steps first: the candidates for today's main step.
countGet commitments
Recurring weekly time: commitments (school runs, training, classes), overhead (sleep, meals) and focus windows.
Takes no parameters.
Set commitments
Replace the list of recurring weekly commitments. days are ISO weekdays 1=Mon..7=Sun; use timesPerWeek instead when there are no fixed days. Pass existing ids to keep them.
commitmentsrequiredGet capacity
Weekly time budget out of 168 hours: overhead, hours committed per area, focus windows, and what is still unaccounted for. Nothing is assumed.
Takes no parameters.
Get planning context
Everything needed to plan a day: goal and pace, next_steps, yesterday's summary and undone items, week balance, days since a hard lever. Defaults to tomorrow. The agent plans; then call set_plan.
dateGet plan
The plan for a day (default today).
dateSet plan
Replace the plan for a day. Exactly one item must have isMainStep=true (the step toward the goal), ideally linked by stepId from next_steps and on a hard lever. Warns when no item is on a hard lever. Other items: paid work, family, workout, etc. with their area. Items that stay keep their done state.
dateitemsrequiredEdit plan item
Change one plan item: mark it done or not done, rename it, change its estimate, area or links, or make it the main step. done=true also completes its linked path step. For a weekly routine, count says how many units this tick did (talked to 2 people today). For a routine, level minimum says only its bad-day minimum was done: it counts for the day, reported apart from full.
areadatedonecountleveltitleitemIdrequiredstepIdleverIdprojectIdisMainStepestimateMinGet routines
Routines: small steps that repeat on fixed weekdays, e.g. "talk with one person on Reddit", or that count up to a number a week, e.g. "talk to 5 people a week", each with an optional bad-day minimum. The server adds them to each matching day's plan, so every device and agent sees the same checks. With no routines yet, returns ready-made starters for the aim's area to offer as they are.
Takes no parameters.
Set routines
Replace the list of routines: small steps that repeat on fixed weekdays. From today on, each one appears as a plan item on its days; tick it with edit_plan_item, skip it for one day with delete_plan_item. set_plan keeps them. days are ISO weekdays 1=Mon..7=Sun, default every day. perWeek makes it a weekly count: each tick adds its count (default 1), and once the week reaches perWeek it leaves the plan until Monday. Link it to the goal with leverId. minimum is the small version for a bad day ("read one thread" for "talk to one person"). Pass existing ids to keep them.
routinesrequiredDelete plan item
Remove one item from a day's plan. A routine item is skipped for that day only.
dateitemIdrequiredSet day mode
Tracking for a day: full (everything recorded, the default) or off (timer and reminders only, nothing stored, no plan required).
datemoderequiredGet now
What is happening now: running sessions with attention shares, the timer, today's main step, today's minutes, the streak and a one-line nudge.
Takes no parameters.
Start session
Start a session of work and, if none is running, the timer. Returns a verdict: ok | stop | plan_first. On "stop" NO session is created: relay the message, ask the user; if they insist, call again with override=true. Several sessions may run at once (two terminals) and share attention by weight. Apps replaying a queued start pass op_id, their own session_id and start_at; an agent leaves all three out.
areataskrequiredop_iditemIdstepIdweightleverIdminutesoverridestart_atdirectionexclusive+3 moreAdd note
Add a one-line note to a running session (this connection's, or session_id). May classify a session that was unclassified.
noterequiredop_idsession_idSplit attention
Set attention weights of the running sessions at once, e.g. {"abc": 0.7, "def": 0.3}. Applies to the whole session. For a finished session use edit_session.
sharesrequiredStop session
Stop a running session with a summary and outcome. Pass session_id from get_now; without it, only this connection's single running session is stopped, never another one. The timer keeps running for other sessions. outcome=done completes the linked plan item or path step. Without end_at or op_id it refuses a session that already ended: use edit_session for that. With end_at (apps delivering a stop queued offline) a second stop on the same session is merged, whatever order they arrive in: the earliest end wins, done wins over any other outcome, the first summary stays and a different later one goes to the notes; the answer says applied: ended | shortened | recorded.
op_idend_atoutcomerequiredsummaryrequiredsession_idattention_shareStop timer
Stop the timer ("pause", "stop", "stand up") and every running session with it. There is no paused state: resuming starts a new session. Pass summary and outcome to save them on the sessions it closes, or call stop_session first. Returns the break length.
outcomesummaryAdd session
Add past time that was not timed: minutes (ending now, or at end), or start+end as HH:MM on date. For calls, children, workout, paid work, or work done away from the agent. Apps replaying a queued entry pass op_id, so a retry does not count the time twice.
endareadatetaskrequiredop_idstartitemIdstepIdweightleverIdminutesexclusive+1 moreEdit session
Fix a session, running or finished: start and end (HH:MM on date; an end before the start means it ran past midnight), task, area, project, lever, step, plan item, summary, outcome, attention share, weight. It is classified again after a change. Use it when a timer was forgotten or a session was labelled wrong.
endareadatetaskstartitemIdstepIdweightleverIdoutcomesummaryexclusive+3 moreDelete session
Delete a session, running or finished, so it no longer counts. Pass session_id from get_now or list_sessions; without it, only this connection's single running session is deleted, never another one.
op_idsession_idList sessions
Sessions in a date range (default the last 7 days), with ids, optionally filtered by area or project.
toareafromprojectIdFind gaps
Untimed stretches of 20+ minutes between the first and last session of a day. Ask the user what happened there.
dateSummarize day
Full summary of a day: wall and attention minutes per area, classification and lever, sessions, gaps, overlaps, overrides.
dateSummarize week
A week, Monday to Sunday (to today for the current week): main-step days, goal share, hard share, area balance, overrides, streak, milestone pace.
dateGet insights
Week (the last 7 days) or month (the last 30 days) ending on to (default today): minutes per day on the aim, other work and life; hard vs comfortable share of aim time; an hour × weekday heatmap of aim minutes (6:00–23:00, Mon–Sun) with its hard part and the peak; main-step days; and comebacks (active days after 2+ days away, over 90 days). Wall minutes of finished sessions, as the app shows them.
torangeFind leaks
Where time leaks: comfortable goal work while the main step waited, overrides, unplanned work, gaps, switching, missed main steps, days without a plan. Default the last 7 days.
tofromGet settings
Timezone, where reminders go (desktop or mobile), timer settings, and which networks get progress posts (post_to).
Takes no parameters.
Set settings
Change any of: timezone (IANA name, e.g. Europe/Madrid; it sets where each day starts and ends), workplace for reminders, stand-up interval workMin, breakMin, longBreakEvery, longBreakMin, standup on/off, post_to (networks for progress posts).
post_tostandupworkMinbreakMintimezoneworkplacelongBreakMinlongBreakEveryAdd device
Register a device token so server reminders (stand up, break over) can reach it. The phone app registers itself; use this for testing or a manual token.
labeltokenrequiredplatformrequiredList devices
Which devices can receive reminders, and when each was last seen.
Takes no parameters.
Delete device
Remove a device so it stops receiving reminders.
tokenrequiredList agents
Agents connected to this AimYear account (Claude, ChatGPT, Claude Code, the AimYear app…): name, client_id, when it connected and when it was last active. current marks the one calling.
Takes no parameters.
Delete agent
Disconnect one agent: its sign-in for this account is deleted and it can no longer read or change anything until it connects again. Only on the user's explicit request. client_id from list_agents.
client_idrequiredAdd social account
Connect WIP (wip.co) or Bluesky so AimYear can post the day's progress there when the user says Post (Pro). WIP: api_key from wip.co settings. Bluesky: handle and an app password (Settings → Privacy and security → App passwords), never the account password. The login is checked, stored encrypted and never shown again. The app's connect screen usually calls this; if the user pastes a key into the chat, use it once and do not repeat it.
handleapi_keynetworkrequiredapp_passwordList social accounts
Connected networks (handle, since when), which of them get progress posts, and whether the user has Pro. Never returns any key or password.
Takes no parameters.
Delete social account
Disconnect a network and delete its saved login. Posts already made stay there.
networkrequiredGet post draft
The day's progress post drafted from the record (aim and progress, main step done or not, minutes on the aim and on hard work, routines, comebacks), at most 300 characters, ending with aimyear.com; and per network whether it is ready, already posted, or skipped because the user already posted there that day. Posts nothing. Pro.
dateAdd post
Post the day's progress, only after the user explicitly said to post. text is the draft as the user edited it (default: the draft from get_post_draft). Goes to the networks chosen in settings, or the ones given. A network where the user already posted that day themselves is skipped. Each day goes to each network once: a retry never posts twice. Today or yesterday only. Pro.
datetextnetworks90% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
Declare a license in the repository or the registry entry.
npm install && npm run build# stdio, one person:claude mcp add --scope user aimyear -e AIMYEAR_UID=<firebase uid> -- node $PWD/dist/index.js# HTTP:AIMYEAR_TOKENS="sometoken=<uid>" PORT=8787 node dist/index.js --httpnpm test # access table guard, anonymous gate and batch smuggling, OAuth discovery, # annotations, Origin, version negotiation, surface lock, domain rulesnpm run test:conformance # a client's path through the real HTTP entry point: initialize and # tools/list without a token, then every tool with a throwaway user # in the real Firestore; cleans upCONFORMANCE_URL=https://mcp.aimyear.com/mcp npm run test:conformance # read-only tools, livenpm run test:oauth # register → consent → real Firebase ID token → code → token (PKCE) → # tool call → refresh rotation, plus every refusal; cleans upnpx tsx test/smoke.ts # the service end to end against Firestoregcloud run deploy aimyear-mcp --source . --project aimyear-app --region us-central1 --quiet