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.
无需输入参数。

io.github.Yahhi由 GitHub 账号 Yahhi 发布v0.12.1更新于 Nov 8, 2026
Accountability partner in your agent: one yearly aim, one main step a day, a timer your agent runs.
AimYear 是一个远程问责服务,让助手维护一个年度目标、规划每日主步骤、运行计时器并回顾时间去向。
Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。
把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。
阅读 https://sourceweft.com/mcp/io.github.Yahhi%2Faimyear-mcp/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。把它添加到你客户端的 mcpServers 配置中。
{
"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.
来源:README.md,提交 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.
无需输入参数。
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.
whyareakindtitle必填metrictargetbaselinedeadline必填Record 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.
datevalue必填milestoneIdSet 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. wip_hashtag only for a lever whose work belongs to one product. Pass existing ids to keep them.
levers必填Set 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. wip_hashtag ties a project to its WIP project (list_wip_projects shows the user's). public: true only when the user says they build this project in public: its sessions with a real summary, and its moves, then go to WIP automatically 10 minutes later unless cancelled (Pro, WIP connected).
projects必填Get path
Milestones with kind, progress (recorded value or steps done), pace (ahead/on_pace/behind) and their steps. With tracks: tracks[], each with its current_milestone_id (every track has its own current milestone); current_milestone_id is then the first open one in list order. Without tracks, current_milestone_id is the one to work on.
无需输入参数。
Set milestones
Replace the list of ordered milestones under the goal (2–6, or up to 6 per track), each countable (metric and target) or measured by its steps, with a due date before the goal deadline; due dates in order within a track. Pass each existing id, or that milestone's steps are deleted. To change one milestone, use edit_milestone.
milestones必填Set tracks
Replace the tracks of the path: 0 to 3 different ways to reach the same aim (e.g. two products that could each earn the target), each with its own milestones and current milestone. reach says how this track alone would reach the aim. milestoneIds puts those milestones into the track, in that order. An empty list makes the path one list again. Use tracks only when the user chooses to work on more than one way; never present one track as the wrong one.
tracks必填Edit milestone
Change one milestone: title, kind (count or steps), metric, target, due date, status or track. status=retired drops it without finishing; done or retired moves its track to the next milestone. Its steps are kept.
duekindtitlemetricstatustargettrackIdmilestoneId必填Add 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_idsteps必填milestoneId必填Edit step
Change one step: title, status (todo/doing/done), order, lever, estimate.
ordertitlestatusstepId必填leverIdestimateMinDelete step
Delete one step from the path.
stepId必填Next steps
Open steps of the current milestone (of every track, taken in turns, each step with its track), 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.
无需输入参数。
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. A recurring life event read from the user's calendar (the children's classes) is one entry with its calendar_event_id, recorded once.
commitments必填Get capacity
Weekly time budget out of 168 hours: overhead, hours committed per area, focus windows, and what is still unaccounted for. Nothing is assumed.
无需输入参数。
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.
dateitems必填Edit 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.
areadatedonecountleveltitleitemId必填stepIdleverIdprojectIdisMainStepestimateMinGet 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.
无需输入参数。
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.
routines必填Delete plan item
Remove one item from a day's plan. A routine item is skipped for that day only.
dateitemId必填Set day mode
Tracking for a day: full (everything recorded, the default) or off (timer and reminders only, nothing stored, no plan required).
datemode必填Get 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.
无需输入参数。
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.
areatask必填op_iditemIdstepIdweightleverIdminutesoverridestart_atdirectionexclusive另有 3 个Add note
Add a one-line note to a running session (this connection's, or session_id). May classify a session that was unclassified.
note必填op_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.
shares必填Stop 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_atoutcome必填summary必填wip_textsession_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, or a calendar event that happened (pass its calendar_event_id: the same event is never logged twice). Apps replaying a queued entry pass op_id, so a retry does not count the time twice.
endareadatetask必填op_idstartitemIdstepIdweightleverIdminutesexclusive另有 2 个Edit 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 个Delete 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_idAdd move
Record a move: one action toward the aim with no duration, e.g. a small fix the user described to you from the phone, or a message sent. task says what changed, in plain words. Classified like a session (pass leverId, stepId, itemId or projectId when known). Past days are fine (date). completes_item: true with item_id ticks that plan item, so a move can finish the main step. Counts toward the weekly target. Use it for work that had no timer; use add_session for timed work.
areadatetask必填timeitemIdstepIdleverIdwip_textprojectIdcompletes_itemList moves
Moves in a date range (default the last 7 days), with ids, whether each was toward the aim and on the hard part.
tofromDelete move
Delete a move recorded by mistake. Pass move_id from list_moves. A plan item it ticked stays ticked.
move_id必填List 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, the default workplace (desktop or mobile; reminders go to every registered device either way), timer settings, which networks get progress posts (post_to), tracking (time, moves or quiet), the weekly target and the weekly review email (weekly_review_email).
无需输入参数。
Set settings
Change any of: timezone (IANA name, e.g. Europe/Madrid; it sets where each day starts and ends), workplace (the default for sessions; reminders go to every device), stand-up interval workMin, breakMin, longBreakEvery, longBreakMin, standup on/off, post_to (networks for progress posts), tracking (time, moves or quiet), weekly_target (days a week toward the aim) and weekly_review_email (on/off).
post_tostandupworkMinbreakMintimezonetrackingworkplacelongBreakMinweekly_targetlongBreakEveryweekly_review_emailAdd 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.
labeltoken必填platform必填List devices
Which devices can receive reminders, and when each was last seen.
无需输入参数。
Delete device
Remove a device so it stops receiving reminders.
token必填List 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.
无需输入参数。
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_id必填Add 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_keynetwork必填app_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.
无需输入参数。
Delete social account
Disconnect a network and delete its saved login. Posts already made stay there.
network必填Get post draft
The day's progress drafted from the record. text is the day's post (Bluesky): it leads with what moved toward the aim (the day's session summaries and moves, in the user's words), then the main step done or not, the aim and its progress, and, if there is room, the week against the target, minutes on the aim and on hard work (never with quiet numbers), routines, comebacks; at most 300 characters, ending with aimyear.com. wip_todos is for WIP: one todo per project worked on that day, about what was done on it, with that project's #hashtag (none when it has no hashtag). Per network: ready, already posted, or skipped because the user already posted there that day. nothing_to_post: true means nothing is recorded toward the aim that day: there is no text; ask the user what they did and record it (add_move) before drafting again. Posts nothing. Pro.
dateAdd post
Post the day's progress, only after the user explicitly said to post. text is the day's post as the user edited it, for Bluesky (default: the draft from get_post_draft). wip_todos are the WIP todos as the user edited them, each with the key from the draft (default: the draft's todos; with text and no wip_todos, WIP gets text as one todo). 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 (on WIP, each project) once: a retry never posts twice. Today or yesterday only. Pro.
datetextnetworkswip_todosList auto posts
The automatic WIP todos of a day (default today), each from a session or move on a project marked public: queued (post_at, still cancellable), posted (url), cancelled, offered (the summary was short, so the phone offered it with a Post button and nothing went out) or failed. Posts nothing.
dateEdit auto post
Change the text of an automatic WIP todo and post it now. Only when the user asked, with their words as they want them on WIP. Also posts an offered, failed or cancelled one when the user says to post it. One todo posts once: a retry never posts twice. Pro.
id必填text必填Delete auto post
Cancel an automatic WIP todo before it posts (the user said not to post it). A todo already on WIP stays there: the answer says so, with its link.
id必填List wip projects
The user's own WIP (wip.co) projects with their hashtags, read through their saved WIP login, and which AimYear project or lever carries each hashtag; plus the AimYear projects and levers without one. Use it once WIP is connected to attach the hashtags (set_projects / set_levers, wip_hashtag), asking the user only when unsure. Posts nothing. Pro.
无需输入参数。
已满足适用检查项的 90%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
请在仓库或注册中心条目中声明许可证。
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