circal-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +123 -0
  2. package/dist/circal-mcp.mjs +4467 -0
  3. package/package.json +35 -0
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # circal-mcp
2
+
3
+ A stdio [MCP](https://modelcontextprotocol.io) server that reads and writes
4
+ circal's own mirror file — the `.json` a browser tab keeps in sync on disk
5
+ through the File System Access API (see `src/lib/mirror.ts` and
6
+ `src/lib/mirrorLink.ts`). It imports circal's own domain layer
7
+ (`src/lib/events.ts`, `recur.ts`, `quickadd.ts`, `mutate.ts`, `backup.ts`, …),
8
+ so an agent and the app can never disagree about what a legal event, an
9
+ occurrence, or a recurrence rule is.
10
+
11
+ Built on `@modelcontextprotocol/server` v2, implementing MCP protocol
12
+ revision 2026-07-28.
13
+
14
+ **The browser tab does not need to be open.** The mirror file is the only
15
+ thing this server reads or writes; if circal's tab is open and connected, it
16
+ picks up the change and its own next write moves the file's `rev` forward
17
+ again, the same as any second writer would.
18
+
19
+ ## Quick start
20
+
21
+ A `FileSystemFileHandle` (what the browser hands circal when the mirror is
22
+ turned on) never exposes the full path it came from: that is deliberate
23
+ browser privacy design, so circal cannot print it for you. `--find` searches
24
+ the places a mirror file usually lands and does that work instead.
25
+
26
+ ```sh
27
+ npx -y circal-mcp --find
28
+ ```
29
+
30
+ It prints every mirror file it located (path, event and calendar counts,
31
+ zone, revision) plus a ready-to-paste config block. Paste that block into
32
+ your client below, swap the path if it picked up the wrong file, then
33
+ restart the client.
34
+
35
+ ### Claude Desktop
36
+
37
+ Add to `claude_desktop_config.json`:
38
+
39
+ ```json
40
+ {
41
+ "mcpServers": {
42
+ "circal": {
43
+ "command": "npx",
44
+ "args": ["-y", "circal-mcp", "--file", "/absolute/path/to/your/calendar.json"]
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Claude Code
51
+
52
+ ```sh
53
+ claude mcp add circal -- npx -y circal-mcp --file /absolute/path/to/your/calendar.json
54
+ ```
55
+
56
+ Or in `.mcp.json`:
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "circal": {
62
+ "command": "npx",
63
+ "args": ["-y", "circal-mcp", "--file", "/absolute/path/to/your/calendar.json"]
64
+ }
65
+ }
66
+ }
67
+ ```
68
+
69
+ ### Cursor
70
+
71
+ Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "circal": {
77
+ "command": "npx",
78
+ "args": ["-y", "circal-mcp", "--file", "/absolute/path/to/your/calendar.json"]
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ No mirror file yet: turn it on from circal's Settings panel, then run
85
+ `--find` again. Run `npx -y circal-mcp --help` for the full flag and
86
+ environment variable list.
87
+
88
+ ## From source (contributors)
89
+
90
+ Building from a checkout instead of the published package:
91
+
92
+ ```sh
93
+ pnpm install
94
+ pnpm build:mcp
95
+ ```
96
+
97
+ Produces the same single-file ESM bundle at `mcp/dist/circal-mcp.mjs`.
98
+ `pnpm mcp` builds and runs it directly, for local testing. Point a client's
99
+ `command`/`args` at `node` and the absolute path to that bundle instead of
100
+ `npx`, with the same `env`/`--file` as above.
101
+
102
+ ## What it refuses, and why
103
+
104
+ - **No `CIRCAL_FILE` (or `--file <path>`).** The path is never guessed — a
105
+ wrong guess here means writing over the wrong year's calendar. The server
106
+ exits with one line on stderr before it ever connects.
107
+ - **A zone mismatch.** Every day boundary in circal is local time. If the
108
+ file's own recorded zone does not match the zone this process is running
109
+ in, reads go through with a warning line; writes are refused, because a
110
+ write meant for "today" could land on the file's yesterday or tomorrow.
111
+ Override with `CIRCAL_ALLOW_ZONE_MISMATCH=1` if you are certain.
112
+ - **A feed calendar.** A calendar with a subscription attached is a mirror of
113
+ somebody else's ICS feed. Its events are pulled, not written — the same
114
+ read-only rule the event editor already enforces — so no tool here can
115
+ create, edit, delete, or tick an event on one.
116
+ - **A repeating event with no `scope`.** `update_event`, `delete_event` and
117
+ similar ask for `"one"`, `"future"`, or `"all"` before touching a series,
118
+ and name the event in the refusal rather than guessing.
119
+ - **A moved file.** Every mutation reads the file, applies the change,
120
+ re-reads to confirm nothing else wrote in between, then writes `rev + 1`
121
+ through a temp file plus rename (so a crash mid-write cannot truncate the
122
+ file). One retry on a conflicting write, then a refusal naming both
123
+ revisions.