Beeminder AI Desktop Extension (for Claude, ChatGPT, et al)

I’ve been using Claude more as part of my productivity setup, so naturally I had to teach it about Beeminder. This is notionally a one-click installation, though your client should also prompt you to enter your personal Beeminder API token.

Rather than simply wrapping the API — which I’ve seen Claude and others wrestle with, since they encounter it without context every single time — I set out to design a set of tools that were more aligned to what I as a Beeminder user would be trying to accomplish. This is a viewpoint that I first encountered here in the context of AI. It’s not quite there as part of the helpful error messages and suchlike, but it should be a solid beginning.

What can you do with it?

Record progress against a goal, either for right now or for yesterday because you forgot. This was unexpectedly tricky to implement because all of the recently-updated flags serverside are very eager, so just like the iOS app it needs to poll the server after adding the datapoint while it waits for the goal to actually update itself.

List all goals, which effectively gets you the Beeminder dashboard. But with a twist, since I have a lot of conservative-slope-plus-autoratchet goals. The tool always checks for an impending ratchet and adjusts the upcoming deadline. Including changing the canonical sort order, so it will differ from the dashboard order if you’ve built up more safe days than tonight’s autoratchet will allow.

Speaking of ‘tonight’, you can configure your habitual start of day, so Claude can guide you about what needs to be done before bedtime today, not just what’s in the red. It uses start of day rather than bedtime as the dividing line, since you’ll need to be out of bed to make progress even on an earlybird goal. Defaults to 7am, so hopefully most folks won’t need to change it.

Beemergencies is the tool that lists those goals that are actually in the red, and presents them as being due either today or tomorrow — in relation to your bedtime/waketime, of course. This tool includes any goals that require data entry because of pessimistic datapoints (which you can add yourself as reminders!)

Calendial is the tool that lists goals that will come due in the week beyond the akrasia horizon. Cunningly, if you’ve put useful descriptive text in the goal’s fine print then Claude can help you make decisions about adjusting those goals.

Source & Acknowledgements

This is of course released with no warranty, expressed or implied. It works for me. The risk is yours.

Inspired by @strickvl’s MCP server and building on @malcolm’s nodejs library. Thank you!

6 Likes

So carefully put together! Thank you!

1 Like

Just out of curiosity, have Beeminder devs considered consistency tokens?

1 Like

If I’m understanding (big if), I don’t think consistency tokens would help us here. What we’re waiting on after adding a datapoint is just a background job that regenerates the graph and recomputes goal stats like safety buffer.

Maybe relatedly, we do support idempotency keys (requestIDs we call them) so that when you submit a datapoint you can be sure you don’t accidentally create duplicates.

If I am not mistaken, recent versions of the ios app need not do this as they are informed by ‘push’ when a goal has been updated and then fetch data for the corresponding goal accordingly.

1 Like

In this case, the thing that I and the iOS app poll for is to see whether the graph has been regenerated (no longer ‘queued’ for update), a field that thankfully is included in the ‘skinny’ view of a goal. That sounds similar to the pattern described as a consistency token, modulo knowing whether the changes have propagated throughout a cluster (which I don’t think we have!).

The initially irksome thing was that there’s an ‘updated at’ field in the user API that is documented as being an order of magnitude faster to check than fetching the goal data, but which isn’t updated after the graph refresh, but rather as soon as the datapoint reaches the server.

I guess I’ve just articulated the bones of what might be a straightforward UVI… thanks! We should perhaps update the updated_at timestamp when the graph generation unqueues.

Another potential UVI uncovered by this work was that we should reflect the potential autoratchet in the various deadline/buffer calculations and in the urgencykey. Otherwise goals can yo-yo in the dashboard as progress is made, as safety is overestimated and then ratcheted back at the deadline. (That would doubtless also be a higher quality calculation, as my simple ‘min’ version doesn’t (and won’t) correctly handle ratcheting across DST boundaries.)

Minor documentation UVI is that the urgencykey blog post talks about first four characters, second four characters, etc., neglecting the semicolon field separators. When I first pointed claudecode at that as the spec, it took the character positions literally.

2 Likes

The current (or fairly recent) code seems to poll in the same way, unless I misread it or that code segment is no longer in active use. I took my 2 second polling loop guidance from the iOS code as being semi-standard.

Also, I don’t see such a push documented in the API other than the usual callback that triggers when a datapoint is CRUD’d — before not after the subsequent recalculations. Not that it’d be convenient to subscribe to such a push from an embedded MCP server, I suspect.

Both are true!

The server does send a push notification to the iOS app when a goal changes, which causes the app to refresh. This is done via the apple iOS push notification system, so isn’t available to other types of client. But also the client doesn’t really trust the server and Apple to get this right, so also polls just in case.

2 Likes

Yeah, I only meant to propose that the server do the polling, like: adding a data point gives you a token that you pass to the endpoint for getting a graph, and that endpoint won’t return a graph until it’s sure that it’s been regenerated with the data point that generated the token.

It sounds like several clients would benefit, and maybe it would be easier for the server than generating that “skinny” response multiple times per graph fetch.

1 Like