npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

circal-mcp

v0.2.1

Published

MCP server for circal — read and write your calendar through its mirror file

Readme

circal-mcp

A stdio MCP server that reads and writes circal's own mirror file — the .json a browser tab or the macOS app keeps in sync on disk, through the File System Access API on the web and the shell's own bridge in the app (see src/lib/mirror.ts, src/lib/mirrorLink.ts and mac/main.swift's MirrorFile). It imports circal's own domain layer (src/lib/events.ts, recur.ts, quickadd.ts, mutate.ts, backup.ts, …), so an agent and the app can never disagree about what a legal event, an occurrence, or a recurrence rule is.

Built on @modelcontextprotocol/server v2, implementing MCP protocol revision 2026-07-28.

The browser tab does not need to be open. The mirror file is the only thing this server reads or writes; if circal's tab is open and connected, it picks up the change and its own next write moves the file's rev forward again, the same as any second writer would.

Quick start

A FileSystemFileHandle (what a browser hands circal when the mirror is turned on there) never exposes the full path it came from: that is deliberate browser privacy design, so circal cannot print it for you. The macOS app does not have this problem — it mints and adopts a mirror at a fixed path on first launch, no gesture required — so --find checks that path first, by name, then falls back to the browser case and searches the places a mirror file usually lands.

npx -y circal-mcp --find

It prints every mirror file it located (path, event and calendar counts, zone, revision) plus a ready-to-paste config block. Paste that block into your client below, swap the path if it picked up the wrong file, then restart the client.

Use a locally built bundle, not npx circal-mcp. The published [email protected] predates several fields in src/lib (free, remindLead, organizer and the guest pair on an event; backupNudge, backupInterval, deviceZone, stampedZones in settings). Every write round-trips the whole document through its reader, so it silently drops those nine keys — measured, same fixture, two bundles. A current server refuses to write a file whose format is newer than its own, so this cannot happen unnoticed any more, but the published bundle is still stale.

pnpm build:mcp   # -> mcp/dist/circal-mcp.mjs

Then use node /absolute/path/to/circal/mcp/dist/circal-mcp.mjs wherever the examples below say npx -y circal-mcp.

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "circal": {
      "command": "npx",
      "args": ["-y", "circal-mcp", "--file", "/absolute/path/to/your/calendar.json"]
    }
  }
}

Claude Code

claude mcp add circal -- npx -y circal-mcp --file /absolute/path/to/your/calendar.json

Or in .mcp.json:

{
  "mcpServers": {
    "circal": {
      "command": "npx",
      "args": ["-y", "circal-mcp", "--file", "/absolute/path/to/your/calendar.json"]
    }
  }
}

Cursor

Add to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):

{
  "mcpServers": {
    "circal": {
      "command": "npx",
      "args": ["-y", "circal-mcp", "--file", "/absolute/path/to/your/calendar.json"]
    }
  }
}

No mirror file yet: on the Mac app there is almost always one already (--find should have caught it); in a browser, turn the mirror on from circal's Settings panel, then run --find again. Run npx -y circal-mcp --help for the full flag and environment variable list.

From source (contributors)

Building from a checkout instead of the published package:

pnpm install
pnpm build:mcp

Produces the same single-file ESM bundle at mcp/dist/circal-mcp.mjs. pnpm mcp builds and runs it directly, for local testing. Point a client's command/args at node and the absolute path to that bundle instead of npx, with the same env/--file as above.

What it refuses, and why

  • No CIRCAL_FILE (or --file <path>). The path is never guessed — a wrong guess here means writing over the wrong year's calendar. The server exits with one line on stderr before it ever connects.
  • A zone mismatch. Every day boundary in circal is local time. If the file's own recorded zone does not match the zone this process is running in, reads go through with a warning line; writes are refused, because a write meant for "today" could land on the file's yesterday or tomorrow. Override with CIRCAL_ALLOW_ZONE_MISMATCH=1 if you are certain.
  • A feed calendar. A calendar with a subscription attached is a mirror of somebody else's ICS feed. Its events are pulled, not written — the same read-only rule the event editor already enforces — so no tool here can create, edit, delete, or tick an event on one.
  • A repeating event with no scope. update_event, delete_event and similar ask for "one", "future", or "all" before touching a series, and name the event in the refusal rather than guessing.
  • A moved file. Every mutation reads the file, applies the change, re-reads to confirm nothing else wrote in between, then writes rev + 1 through a temp file plus rename (so a crash mid-write cannot truncate the file). One retry on a conflicting write, then a refusal naming both revisions.