@davidwoods/skylight-mcp 0.0.0-stage → 1.7.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 (168) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +583 -2
  3. package/THIRD_PARTY_NOTICES.md +38 -0
  4. package/dist/api/auth.d.ts +34 -0
  5. package/dist/api/auth.d.ts.map +1 -0
  6. package/dist/api/auth.js +328 -0
  7. package/dist/api/auth.js.map +1 -0
  8. package/dist/api/client.d.ts +103 -0
  9. package/dist/api/client.d.ts.map +1 -0
  10. package/dist/api/client.js +324 -0
  11. package/dist/api/client.js.map +1 -0
  12. package/dist/api/constants.d.ts +4 -0
  13. package/dist/api/constants.d.ts.map +1 -0
  14. package/dist/api/constants.js +4 -0
  15. package/dist/api/constants.js.map +1 -0
  16. package/dist/api/credential-store.d.ts +38 -0
  17. package/dist/api/credential-store.d.ts.map +1 -0
  18. package/dist/api/credential-store.js +273 -0
  19. package/dist/api/credential-store.js.map +1 -0
  20. package/dist/api/credential-types.d.ts +15 -0
  21. package/dist/api/credential-types.d.ts.map +1 -0
  22. package/dist/api/credential-types.js +11 -0
  23. package/dist/api/credential-types.js.map +1 -0
  24. package/dist/api/endpoints/calendar.d.ts +36 -0
  25. package/dist/api/endpoints/calendar.d.ts.map +1 -0
  26. package/dist/api/endpoints/calendar.js +74 -0
  27. package/dist/api/endpoints/calendar.js.map +1 -0
  28. package/dist/api/endpoints/categories.d.ts +32 -0
  29. package/dist/api/endpoints/categories.d.ts.map +1 -0
  30. package/dist/api/endpoints/categories.js +77 -0
  31. package/dist/api/endpoints/categories.js.map +1 -0
  32. package/dist/api/endpoints/chores.d.ts +50 -0
  33. package/dist/api/endpoints/chores.d.ts.map +1 -0
  34. package/dist/api/endpoints/chores.js +106 -0
  35. package/dist/api/endpoints/chores.js.map +1 -0
  36. package/dist/api/endpoints/devices.d.ts +6 -0
  37. package/dist/api/endpoints/devices.d.ts.map +1 -0
  38. package/dist/api/endpoints/devices.js +10 -0
  39. package/dist/api/endpoints/devices.js.map +1 -0
  40. package/dist/api/endpoints/frames.d.ts +6 -0
  41. package/dist/api/endpoints/frames.d.ts.map +1 -0
  42. package/dist/api/endpoints/frames.js +10 -0
  43. package/dist/api/endpoints/frames.js.map +1 -0
  44. package/dist/api/endpoints/lists.d.ts +55 -0
  45. package/dist/api/endpoints/lists.d.ts.map +1 -0
  46. package/dist/api/endpoints/lists.js +125 -0
  47. package/dist/api/endpoints/lists.js.map +1 -0
  48. package/dist/api/endpoints/meals.d.ts +154 -0
  49. package/dist/api/endpoints/meals.d.ts.map +1 -0
  50. package/dist/api/endpoints/meals.js +194 -0
  51. package/dist/api/endpoints/meals.js.map +1 -0
  52. package/dist/api/endpoints/messages.d.ts +98 -0
  53. package/dist/api/endpoints/messages.d.ts.map +1 -0
  54. package/dist/api/endpoints/messages.js +116 -0
  55. package/dist/api/endpoints/messages.js.map +1 -0
  56. package/dist/api/endpoints/misc.d.ts +27 -0
  57. package/dist/api/endpoints/misc.d.ts.map +1 -0
  58. package/dist/api/endpoints/misc.js +19 -0
  59. package/dist/api/endpoints/misc.js.map +1 -0
  60. package/dist/api/endpoints/photos.d.ts +13 -0
  61. package/dist/api/endpoints/photos.d.ts.map +1 -0
  62. package/dist/api/endpoints/photos.js +10 -0
  63. package/dist/api/endpoints/photos.js.map +1 -0
  64. package/dist/api/endpoints/rewards.d.ts +49 -0
  65. package/dist/api/endpoints/rewards.d.ts.map +1 -0
  66. package/dist/api/endpoints/rewards.js +106 -0
  67. package/dist/api/endpoints/rewards.js.map +1 -0
  68. package/dist/api/endpoints/shape.d.ts +11 -0
  69. package/dist/api/endpoints/shape.d.ts.map +1 -0
  70. package/dist/api/endpoints/shape.js +34 -0
  71. package/dist/api/endpoints/shape.js.map +1 -0
  72. package/dist/api/endpoints/taskbox.d.ts +13 -0
  73. package/dist/api/endpoints/taskbox.d.ts.map +1 -0
  74. package/dist/api/endpoints/taskbox.js +22 -0
  75. package/dist/api/endpoints/taskbox.js.map +1 -0
  76. package/dist/api/oauth.d.ts +62 -0
  77. package/dist/api/oauth.d.ts.map +1 -0
  78. package/dist/api/oauth.js +206 -0
  79. package/dist/api/oauth.js.map +1 -0
  80. package/dist/api/types.d.ts +277 -0
  81. package/dist/api/types.d.ts.map +1 -0
  82. package/dist/api/types.js +5 -0
  83. package/dist/api/types.js.map +1 -0
  84. package/dist/cli.d.ts +23 -0
  85. package/dist/cli.d.ts.map +1 -0
  86. package/dist/cli.js +105 -0
  87. package/dist/cli.js.map +1 -0
  88. package/dist/config.d.ts +27 -0
  89. package/dist/config.d.ts.map +1 -0
  90. package/dist/config.js +78 -0
  91. package/dist/config.js.map +1 -0
  92. package/dist/context.d.ts +17 -0
  93. package/dist/context.d.ts.map +1 -0
  94. package/dist/context.js +17 -0
  95. package/dist/context.js.map +1 -0
  96. package/dist/doctor.d.ts +19 -0
  97. package/dist/doctor.d.ts.map +1 -0
  98. package/dist/doctor.js +109 -0
  99. package/dist/doctor.js.map +1 -0
  100. package/dist/index.d.ts +3 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +24 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/server.d.ts +7 -0
  105. package/dist/server.d.ts.map +1 -0
  106. package/dist/server.js +29 -0
  107. package/dist/server.js.map +1 -0
  108. package/dist/tools/calendar.d.ts +3 -0
  109. package/dist/tools/calendar.d.ts.map +1 -0
  110. package/dist/tools/calendar.js +298 -0
  111. package/dist/tools/calendar.js.map +1 -0
  112. package/dist/tools/chores.d.ts +3 -0
  113. package/dist/tools/chores.d.ts.map +1 -0
  114. package/dist/tools/chores.js +349 -0
  115. package/dist/tools/chores.js.map +1 -0
  116. package/dist/tools/family.d.ts +3 -0
  117. package/dist/tools/family.d.ts.map +1 -0
  118. package/dist/tools/family.js +182 -0
  119. package/dist/tools/family.js.map +1 -0
  120. package/dist/tools/lists.d.ts +3 -0
  121. package/dist/tools/lists.d.ts.map +1 -0
  122. package/dist/tools/lists.js +481 -0
  123. package/dist/tools/lists.js.map +1 -0
  124. package/dist/tools/meals.d.ts +3 -0
  125. package/dist/tools/meals.d.ts.map +1 -0
  126. package/dist/tools/meals.js +491 -0
  127. package/dist/tools/meals.js.map +1 -0
  128. package/dist/tools/messages.d.ts +3 -0
  129. package/dist/tools/messages.d.ts.map +1 -0
  130. package/dist/tools/messages.js +195 -0
  131. package/dist/tools/messages.js.map +1 -0
  132. package/dist/tools/misc.d.ts +3 -0
  133. package/dist/tools/misc.d.ts.map +1 -0
  134. package/dist/tools/misc.js +102 -0
  135. package/dist/tools/misc.js.map +1 -0
  136. package/dist/tools/photos.d.ts +3 -0
  137. package/dist/tools/photos.d.ts.map +1 -0
  138. package/dist/tools/photos.js +42 -0
  139. package/dist/tools/photos.js.map +1 -0
  140. package/dist/tools/planning.d.ts +15 -0
  141. package/dist/tools/planning.d.ts.map +1 -0
  142. package/dist/tools/planning.js +303 -0
  143. package/dist/tools/planning.js.map +1 -0
  144. package/dist/tools/registry.d.ts +39 -0
  145. package/dist/tools/registry.d.ts.map +1 -0
  146. package/dist/tools/registry.js +142 -0
  147. package/dist/tools/registry.js.map +1 -0
  148. package/dist/tools/rewards.d.ts +3 -0
  149. package/dist/tools/rewards.d.ts.map +1 -0
  150. package/dist/tools/rewards.js +339 -0
  151. package/dist/tools/rewards.js.map +1 -0
  152. package/dist/tools/tasks.d.ts +3 -0
  153. package/dist/tools/tasks.d.ts.map +1 -0
  154. package/dist/tools/tasks.js +68 -0
  155. package/dist/tools/tasks.js.map +1 -0
  156. package/dist/utils/dates.d.ts +26 -0
  157. package/dist/utils/dates.d.ts.map +1 -0
  158. package/dist/utils/dates.js +109 -0
  159. package/dist/utils/dates.js.map +1 -0
  160. package/dist/utils/errors.d.ts +45 -0
  161. package/dist/utils/errors.d.ts.map +1 -0
  162. package/dist/utils/errors.js +102 -0
  163. package/dist/utils/errors.js.map +1 -0
  164. package/dist/utils/schedule.d.ts +86 -0
  165. package/dist/utils/schedule.d.ts.map +1 -0
  166. package/dist/utils/schedule.js +189 -0
  167. package/dist/utils/schedule.js.map +1 -0
  168. package/package.json +76 -4
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 EagleByte
4
+ Copyright (c) 2026 David Woods
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,584 @@
1
- # Temporary Holding Version
1
+ # Skylight MCP Server
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An MCP (Model Context Protocol) server for the Skylight Calendar. Lets Claude
4
+ (including Claude Duet, via the hosted mode) read and plan against your family
5
+ calendar, chores, lists, meals and rewards.
6
+
7
+ > **Unofficial.** This project is not affiliated with or endorsed by Skylight.
8
+ > Skylight has no public API; this talks to the same private endpoints their
9
+ > own apps use, discovered by observing app traffic. It can stop working or be
10
+ > blocked at any time, without notice. Use it with your own account, at your
11
+ > own risk, and keep request volume modest.
12
+ >
13
+ > Forked from [TheEagleByte/skylight-mcp](https://github.com/TheEagleByte/skylight-mcp)
14
+ > (MIT). The sign-in and credential-store design follows
15
+ > [kamilio/skylight-calendar-agent](https://github.com/kamilio/skylight-calendar-agent)
16
+ > (MIT). See `THIRD_PARTY_NOTICES.md`.
17
+
18
+ ## Features
19
+
20
+ - **Calendar**: read events and connected calendars; create, update and delete events
21
+ - **Planning**: one-call week overview, overlap detection, free-slot search, bulk meal planning
22
+ - **Meals** (Plus): see, add, rename, move and delete scheduled meals; browse and edit recipes
23
+ - **Messages**: sending to specific devices, scheduling, repeating, voice notes, editing a message that was sent to specific profiles (the app's edit form does not allow changing recipients), and read-aloud (the option exists on `send_message` but was not tested live).
24
+ - **Chores and tasks**: read, create, update and delete chores; add to the task box
25
+ - **Lists**: read, create, update and delete lists and items
26
+ - **Family**: read profiles, devices and household info
27
+ - **Rewards** (Plus): read points and rewards; create, update, redeem and delete
28
+
29
+ See [What is supported](#what-is-supported) for what is tested, what is not, and
30
+ what the hosted server leaves out.
31
+
32
+ ## Quick Start
33
+
34
+ You need Node 20+, a Skylight account, and an MCP client (Claude desktop, Claude
35
+ Code, or any other). Nothing to clone or build.
36
+
37
+ ### 1. Sign in (once)
38
+
39
+ ```bash
40
+ npx -y @davidwoods/skylight-mcp auth login
41
+ ```
42
+
43
+ This prints a Skylight sign-in URL. Open it in your browser and sign in. The
44
+ browser bounces through `https://ourskylight.com/welcome?code=...` and on to
45
+ the Skylight home page too fast to copy. Get it from your browser history:
46
+ `Cmd-Y` (Mac) / `Ctrl-H`, search `welcome?code`, right-click the newest
47
+ Skylight result, Copy link address. Then:
48
+
49
+ ```bash
50
+ npx -y @davidwoods/skylight-mcp auth complete
51
+ ```
52
+
53
+ and paste it. Your password never touches this program; only the resulting
54
+ OAuth tokens are stored, encrypted with a key in the macOS Keychain (plain
55
+ `0600` file on Linux/Windows). macOS may ask once to allow Keychain access.
56
+ `auth status` and `auth logout` do what they say.
57
+
58
+ ### 2. Find your frame ID and timezone
59
+
60
+ Your frame ID is the number in `https://ourskylight.com/my-skylight/<number>`
61
+ after you sign in to the Skylight web app (see
62
+ [Finding your Frame ID](#finding-your-frame-id)). Your timezone is an IANA name
63
+ such as `America/Chicago`. **Set it**: it defaults to `America/New_York`, and
64
+ event times and date windows come out wrong anywhere else.
65
+
66
+ ### 3. Add to your MCP client
67
+
68
+ **Claude desktop** (Settings → Developer → Edit Config), or any client that uses `mcp.json`:
69
+ ```json
70
+ {
71
+ "mcpServers": {
72
+ "skylight": {
73
+ "command": "npx",
74
+ "args": ["-y", "@davidwoods/skylight-mcp"],
75
+ "env": {
76
+ "SKYLIGHT_FRAME_ID": "your_frame_id",
77
+ "SKYLIGHT_TIMEZONE": "America/New_York"
78
+ }
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ **Claude Code:**
85
+ ```bash
86
+ claude mcp add skylight \
87
+ -e SKYLIGHT_FRAME_ID=your_frame_id -e SKYLIGHT_TIMEZONE=America/New_York \
88
+ -- npx -y @davidwoods/skylight-mcp
89
+ ```
90
+
91
+ Restart the client fully (Cmd-Q on Mac), then ask it "what's on the family
92
+ calendar this week?"
93
+
94
+ ### 4. Check it
95
+
96
+ ```bash
97
+ SKYLIGHT_FRAME_ID=your_frame_id SKYLIGHT_TIMEZONE=America/New_York \
98
+ npx -y @davidwoods/skylight-mcp doctor
99
+ ```
100
+
101
+ `doctor` checks Node, the frame ID, the timezone, your sign-in and one real call
102
+ to Skylight, and says what to fix. It never prints tokens or your frame ID.
103
+ Add `--offline` to skip the network call.
104
+
105
+ ### From source (contributors)
106
+
107
+ ```bash
108
+ git clone https://github.com/dwooods/skylight-mcp.git
109
+ cd skylight-mcp && npm install && npm run build
110
+ node dist/index.js auth login # same steps as above, using node dist/index.js
111
+ ```
112
+
113
+ In the client config use `"command": "node", "args": ["/path/to/skylight-mcp/dist/index.js"]`.
114
+
115
+ ### Windows (not yet verified)
116
+
117
+ Windows is untested as of this writing; treat these as expected, not confirmed.
118
+
119
+ - `npx` is a `.cmd` file on Windows, so in a client config use
120
+ `"command": "cmd", "args": ["/c", "npx", "-y", "@davidwoods/skylight-mcp"]`.
121
+ - Set variables in PowerShell like this, then run the command on the next line:
122
+ `$env:SKYLIGHT_FRAME_ID="your_frame_id"; $env:SKYLIGHT_TIMEZONE="America/New_York"`.
123
+ - The sign-in is stored unencrypted in `%APPDATA%\skylight-mcp\credential.json`.
124
+ There is no Keychain equivalent wired in, and Windows ignores Unix `0600`
125
+ permissions. Only your Windows user profile protects it.
126
+
127
+ ### Troubleshooting
128
+
129
+ | Symptom | Likely cause and fix |
130
+ |---|---|
131
+ | Server fails to start, mentions configuration | `SKYLIGHT_FRAME_ID` missing. Run `doctor`. |
132
+ | "Not signed in" | Run `auth login` then `auth complete`. Logins expire after 10 minutes. |
133
+ | macOS Keychain error | The server refuses to fall back to plaintext. Unlock the Keychain or allow access when prompted. |
134
+ | Events show at the wrong hour | `SKYLIGHT_TIMEZONE` unset (defaults to New York). Set your zone. |
135
+ | Tools missing after an update | Fully quit and reopen the client; it caches the tool list. For a hosted connector use its "Refresh tools list". |
136
+ | Worked yesterday, fails today | Skylight has no public API and may have changed. Run `doctor`, then check the issues. |
137
+
138
+ ### Instructions for AI
139
+
140
+ Copy this into your AI's custom instructions or system prompt:
141
+
142
+ > You have access to the Skylight MCP server. Skylight is a smart family calendar display that shows calendars, chores, grocery lists, meals, and rewards. Use the Skylight tools to help manage family schedules and organization.
143
+ >
144
+ > Tips:
145
+ > - Call `get_family_members` before assigning chores to get member names
146
+ > - Grocery items default to the main grocery list if no list specified
147
+ > - Dates accept "today", "tomorrow", day names, or YYYY-MM-DD format
148
+ > - Some tools (rewards, meals, photos) require Skylight Plus subscription
149
+
150
+ ## Prerequisites
151
+
152
+ - Node.js 20+
153
+ - A Skylight account with an active subscription
154
+ - Your Skylight Frame ID (see [Finding your Frame ID](#finding-your-frame-id))
155
+
156
+ ## Authentication
157
+
158
+ Sources are tried in this order:
159
+
160
+ 1. **`SKYLIGHT_TOKEN`** - an explicit token in the environment, if set.
161
+ 2. **Stored OAuth credential** (recommended) - created by `skylight-mcp auth login`
162
+ / `auth complete`. The access token is refreshed automatically; refresh tokens
163
+ rotate and the rotated token is persisted.
164
+ 3. **`SKYLIGHT_EMAIL` + `SKYLIGHT_PASSWORD`** (deprecated) - the server replays
165
+ Skylight's web login form itself. This works but puts your password in your
166
+ MCP client config in plaintext. Prefer the browser sign-in above.
167
+
168
+ If none is present the server fails with instructions rather than starting.
169
+
170
+ Where the credential lives:
171
+
172
+ | Platform | Location | Protection |
173
+ |----------|----------|------------|
174
+ | macOS | `~/Library/Application Support/skylight-mcp/credential.enc.json` | AES-256-GCM; key in Keychain (`skylight-mcp.encryption-key`) |
175
+ | Linux / other | `$XDG_CONFIG_HOME/skylight-mcp/credential.json` (default `~/.config/skylight-mcp/`) | `0600` file in a `0700` directory, no encryption |
176
+ | Windows | `%APPDATA%\\skylight-mcp\\credential.json` | Not encrypted. Windows ignores `0600`; only your user profile's own permissions protect it. Untested, see below. |
177
+
178
+ If the Keychain is unavailable on macOS the server refuses to read or write the
179
+ credential; it never silently downgrades to plaintext.
180
+
181
+ ### Finding your Frame ID
182
+
183
+ The frame ID is the household identifier. The easy way: after signing in, the
184
+ Skylight web app lands on `https://ourskylight.com/my-skylight/<number>`. That
185
+ number is your frame ID. Treat it as a secret.
186
+
187
+ The hard way, if that URL doesn't appear:
188
+
189
+ 1. Use a proxy tool ([Proxyman](https://proxyman.io/), [Charles](https://www.charlesproxy.com/), or [mitmproxy](https://mitmproxy.org/))
190
+ 2. Capture any API request from the Skylight app
191
+ 3. Look at the URL path: `/api/frames/{frameId}/...`
192
+ 4. Example: `/api/frames/abc123/chores` → frame ID is `abc123`
193
+
194
+ ## Configuration
195
+
196
+ | Variable | Required | Description |
197
+ |----------|----------|-------------|
198
+ | `SKYLIGHT_TOKEN` | No | Explicit API token; overrides the stored credential |
199
+ | `SKYLIGHT_AUTH_TYPE` | No | `bearer` (default) or `basic` (only with `SKYLIGHT_TOKEN`) |
200
+ | `SKYLIGHT_EMAIL` | No (deprecated) | Fallback email/password login |
201
+ | `SKYLIGHT_PASSWORD` | No (deprecated) | Fallback email/password login |
202
+ | `SKYLIGHT_FRAME_ID` | Yes | Your household frame ID |
203
+ | `SKYLIGHT_TIMEZONE` | No | Default timezone (default: `America/New_York`) |
204
+
205
+ ### Auth Notes
206
+
207
+ - The API host is pinned to `https://app.ourskylight.com`. No environment
208
+ variable or `.env` file can redirect requests (and the bearer token) elsewhere.
209
+ - `auth logout` deletes the local credential only; Skylight has no public way to
210
+ revoke a session, so change your password if a credential may have leaked.
211
+ - Skylight has no official API. This uses the same OAuth client the Skylight
212
+ app uses, and it may break or be blocked at any time.
213
+
214
+ ## Hosted server (Claude Duet, Claude on other devices)
215
+
216
+ The same code also runs as a remote MCP server on Cloudflare Workers, which is
217
+ how Claude Duet (and any Claude client that can't reach your Mac) connects.
218
+ One Worker is both the OAuth authorization server Claude talks to and the MCP
219
+ resource server. Signing in is the same browser-paste flow as the CLI; your
220
+ Skylight password never reaches the Worker, and the Skylight tokens are kept
221
+ encrypted in the OAuth grant, not in a database you manage.
222
+
223
+ ### Which tools the hosted server exposes, and why
224
+
225
+ By default the hosted server registers **read and create/update tools only**:
226
+ no `delete_*` tools and no photo uploads. The local (stdio) server registers
227
+ everything. This is a deliberate choice, not a limitation of the Worker:
228
+
229
+ - The hosted server is what Claude Duet drives proactively, sometimes
230
+ unattended. A misread instruction that *creates* a wrong chore is a
231
+ nuisance; one that *deletes* a calendar event is data loss with no undo
232
+ from Claude. Leaving deletes off the hosted path caps the worst outcome at
233
+ "something extra to clean up".
234
+ - Photo tools are uploads the Worker has no reason to carry.
235
+
236
+ If you want full parity on the hosted server, change one line in
237
+ `src/worker/mcp.ts` and redeploy:
238
+
239
+ ```ts
240
+ export const HOSTED_EFFECTS: readonly ToolEffect[] = ["read", "write", "destructive"];
241
+ ```
242
+
243
+ and set `photos: true` in the same file if you also want photo uploads.
244
+ Every tool's classification lives in `src/tools/registry.ts`, so you can also
245
+ carve out a profile of your own (read-only, for example, is `["read"]`).
246
+
247
+ If you run both servers in the Claude desktop app, Claude sees two sets of
248
+ mostly duplicate tools and may use either for writes. Disable the local one
249
+ in Settings → Connectors when you do not need the delete or photo tools, or
250
+ say "use the local Skylight server" when you do.
251
+
252
+ ### Deploy (once)
253
+
254
+ You need a free Cloudflare account. From a clone of this repo:
255
+
256
+ ```bash
257
+ npm install
258
+ npx wrangler login
259
+ npm run worker:setup # add -- --dry-run first to see the steps
260
+ ```
261
+
262
+ `worker:setup` creates your KV namespace, writes a gitignored
263
+ `wrangler.local.jsonc` with your real ID (the committed `wrangler.jsonc` keeps a
264
+ placeholder, so nothing account-specific can be committed), asks you for a
265
+ sign-in passphrase, deploys, sets `WORKER_URL`, deploys again and prints the
266
+ connector URL. Re-deploy later with `npm run worker:deploy:local`.
267
+
268
+ Doing it by hand instead:
269
+
270
+ ```bash
271
+ cp wrangler.jsonc wrangler.local.jsonc # gitignored: your real IDs live only here
272
+ npx wrangler kv namespace create OAUTH_KV # paste the id into wrangler.local.jsonc, not wrangler.jsonc
273
+ npx wrangler secret put SETUP_PASSPHRASE -c wrangler.local.jsonc # 12+ chars; required to link an account
274
+ npm run worker:deploy:local # prints the Worker's URL
275
+ npx wrangler secret put WORKER_URL -c wrangler.local.jsonc # paste that URL (origin only, no path)
276
+ npm run worker:deploy:local
277
+ ```
278
+
279
+ `WORKER_URL` must be exactly the public origin of the Worker; every token is
280
+ bound to it. It is a secret rather than a `vars` entry so the server's
281
+ address is not published with the source.
282
+
283
+ ### Connect from Claude
284
+
285
+ In Claude (or Duet) → Settings → Connectors → Add custom connector:
286
+
287
+ - Name: `Skylight`
288
+ - URL: `https://skylight-mcp.<your-subdomain>.workers.dev/mcp`
289
+
290
+ Claude opens the consent page. Enter the setup passphrase, your frame ID and
291
+ timezone, then follow the two-step Skylight sign-in and paste the redirect
292
+ URL. Connectors are shared across your Claude account, so Duet sees it too.
293
+
294
+ ### What is stored where
295
+
296
+ | Thing | Where | Protection |
297
+ |---|---|---|
298
+ | Skylight access + refresh tokens | OAuth grant props in `OAUTH_KV` | Encrypted by workers-oauth-provider; the key is derived from the token Claude holds, so KV alone is not enough |
299
+ | PKCE verifier during sign-in | Upstream transaction in `OAUTH_KV` | Bound to your browser by a `__Host-` cookie; single use; 10-minute TTL |
300
+ | Setup passphrase | Worker secret | Never in the repo or `wrangler.jsonc` |
301
+ | Nothing else | | The Worker has no database and no logs of tokens |
302
+
303
+ To revoke: remove the connector in Claude (revokes the grant), or
304
+ `npx wrangler kv key list --binding OAUTH_KV` and delete, or change your
305
+ Skylight password.
306
+
307
+ ## What is supported
308
+
309
+ Status vocabulary, used honestly:
310
+
311
+ - **Live-tested**: run against a real Skylight frame by this project, with the result checked by reading back.
312
+ - **Inherited**: came from the upstream project and works there, but this project has not re-run it live.
313
+ - **Mocked only**: covered by this project's mocked tests; not yet run against real data.
314
+
315
+ Skylight has no public API. Everything below can break when Skylight ships a
316
+ web release, which happens without notice. The web app version and API version
317
+ last captured are recorded in the repository's build log.
318
+
319
+ | Area | What works | Hosted server (Claude Duet) | Status |
320
+ |------|------------|------------------------------|--------|
321
+ | Calendar | Read events and source calendars; create (with profiles), update, delete events | Everything except delete | Read, create and delete live-tested; update inherited |
322
+ | Planning | Week overview, conflicts, free slots, bulk meal plan | All four | Week overview live-tested; the rest mocked only |
323
+ | Meals (Plus) | Read categories and meals; create, rename, move, delete one-off meals | Everything except delete | Live-tested |
324
+ | Recipes (Plus) | Read, create, update, delete recipes; push ingredients to the grocery list | Everything except delete | Inherited |
325
+ | Messages | Read; post to everyone or to named profiles (silent by default); edit text and sender (everyone-messages only); delete | All four, delete included | Live-tested |
326
+ | Chores | Read, create, update, delete | Everything except delete | Read live-tested; writes inherited |
327
+ | Task box | Add a task | Yes | Inherited |
328
+ | Lists | Read lists and items; create, update, delete lists and items | Everything except delete | Inherited |
329
+ | Family and devices | Profiles with IDs, avatars, colours, household info, devices | Yes | Profiles live-tested; the rest inherited |
330
+ | Rewards (Plus) | Read points and rewards; create, update, redeem, unredeem, delete | Everything except delete | Inherited |
331
+ | Photos (Plus) | List albums | No | Inherited |
332
+
333
+ ### Not supported
334
+
335
+ Nothing here is hidden behind a flag. It is either unavailable from Skylight or not yet captured.
336
+
337
+ - **Messages**: deleting one (Skylight offers no way), sending to specific profiles or devices, scheduling, repeating, voice notes, and read-aloud (the option exists on `send_message` but was not tested live).
338
+ - **Meals**: editing or deleting a repeating meal (it is refused, because the behaviour of changing one occurrence of a series was not captured), saving a meal to the recipe box from the edit form, and Skylight's own "Plan Meals" feature.
339
+ - **Task box**: listing, editing or completing tasks. It is create-only.
340
+ - **Profiles**: creating or editing them. Read-only.
341
+ - **Settings, account, notifications, sleep schedule, device navigation**: none of these are exposed.
342
+ - **Photos**: uploads and anything beyond listing albums.
343
+ - **Accounts with more than one frame**: the server is pinned to one frame ID.
344
+ - **Credentials**: sign-in, sign-out and token access are CLI commands (`skylight-mcp auth ...`) and are deliberately not MCP tools.
345
+
346
+ ### Keeping this honest
347
+
348
+ Every tool must appear in the tables below with the effect it really has, and a
349
+ test fails if one is missing or mislabelled. When you add, change or remove a
350
+ tool, update this section in the same change.
351
+
352
+ ## Available Tools
353
+
354
+ Effects: **read** changes nothing; **write** creates or edits and can be edited
355
+ again; **destructive** cannot be undone from the API. The hosted server registers
356
+ read and write only, so destructive tools exist on the local server alone.
357
+
358
+ ### Planning Tools
359
+
360
+ Built for "what does the week look like and what is for dinner" questions.
361
+ One call returns calendar, chores/tasks, meals and lists, merged per day.
362
+
363
+ | Tool | Effect | Description |
364
+ |------|--------|-------------|
365
+ | `get_week_overview` | read | Per-day events, chores/tasks (status, assignee, points), meals, overdue chores, conflicts and list summaries in one compact call. Omits owner emails and calendar IDs. |
366
+ | `find_conflicts` | read | Timed events that overlap in a date range. All-day events never conflict. |
367
+ | `find_free_slots` | read | Gaps of at least N minutes inside a daily window (default 08:00 to 21:00). |
368
+ | `plan_week_meals` | write | Fill several meal slots at once. `dryRun` previews; occupied slots are skipped, never overwritten; all entries are validated before anything is written; optional grocery push for recipes. |
369
+
370
+ Times are computed in the frame's time zone, including DST changes.
371
+
372
+ ### Message Tools
373
+
374
+ Messages are the notes shown on the frame (Skylight's web app calls them "nudges").
375
+
376
+ | Tool | Effect | Description |
377
+ |------|--------|-------------|
378
+ | `get_messages` | read | Messages in a date range with time, text, recipients and sender. Never returns the presigned audio URLs. |
379
+ | `send_message` | write | Post a message to the frame now, on all devices. Previews by default and posts only with `confirm: true`. Read aloud is **off** unless asked for. `to` names the profiles to send to (see `get_family_members`); omit it for everyone. Optional sender profile. Up to 300 characters. The app has no delete button, but `delete_message` removes one. |
380
+ | `update_message` | write | Edit the text (and optionally the sender) of an existing message. Only messages sent to everyone on all devices that don't repeat; others must be edited in the app. |
381
+ | `delete_message` | destructive | Delete a message. The app has no delete button; this uses `DELETE /nudges/{id}`, documented by other Skylight clients and verified on a real frame. Available on both the local and hosted servers (the one destructive tool the hosted server offers). Previews unless `confirm: true`, then reads back and reports success only if the message is gone. |
382
+
383
+ ### Calendar Tools
384
+
385
+ | Tool | Effect | Description |
386
+ |------|--------|-------------|
387
+ | `get_calendar_events` | read | Events for a date range |
388
+ | `get_source_calendars` | read | Connected calendar sources (Google, iCloud, etc.) |
389
+ | `create_calendar_event` | write | Add an event. `profiles` assigns it to named family members. Local times are sent with the frame's UTC offset. |
390
+ | `update_calendar_event` | write | Change an event, including who it is assigned to (`profiles`) |
391
+ | `delete_calendar_event` | destructive | Delete an event. Local server only. |
392
+
393
+ ### Chore Tools
394
+
395
+ | Tool | Effect | Description |
396
+ |------|--------|-------------|
397
+ | `get_chores` | read | Chores with optional filters (date, assignee, status) |
398
+ | `create_chore` | write | Create a chore, optionally recurring |
399
+ | `update_chore` | write | Rename, reschedule, reassign, complete or reopen a chore |
400
+ | `delete_chore` | destructive | Delete a chore. Local server only. |
401
+
402
+ ### Task Tools
403
+
404
+ | Tool | Effect | Description |
405
+ |------|--------|-------------|
406
+ | `create_task` | write | Add a task to the task box (create-only; there is no list tool) |
407
+
408
+ ### List Tools
409
+
410
+ | Tool | Effect | Description |
411
+ |------|--------|-------------|
412
+ | `get_lists` | read | All lists |
413
+ | `get_list_items` | read | Items in one list |
414
+ | `create_list` | write | Create a list |
415
+ | `update_list` | write | Rename or change a list |
416
+ | `create_list_item` | write | Add an item |
417
+ | `update_list_item` | write | Edit or tick off an item |
418
+ | `delete_list` | destructive | Delete a list. Local server only. |
419
+ | `delete_list_item` | destructive | Delete an item. Local server only. |
420
+
421
+ ### Family Tools
422
+
423
+ | Tool | Effect | Description |
424
+ |------|--------|-------------|
425
+ | `get_family_members` | read | Family member profiles, with the IDs and names that `to` and `profiles` use |
426
+ | `get_frame_info` | read | Household/frame information |
427
+ | `get_devices` | read | Skylight devices |
428
+ | `get_avatars` | read | Available profile avatars |
429
+ | `get_colors` | read | Available profile colours |
430
+
431
+ ### Meal Tools
432
+
433
+ Meals and recipes need Skylight Plus.
434
+
435
+ | Tool | Effect | Description |
436
+ |------|--------|-------------|
437
+ | `get_meal_categories` | read | Breakfast, Lunch, Dinner, Snack and their IDs |
438
+ | `get_meal_sittings` | read | Scheduled meals in a date range, with sitting IDs |
439
+ | `create_meal_sitting` | write | Schedule a meal on a date (free text or a saved recipe) |
440
+ | `update_meal_sitting` | write | Rename, re-categorise, add a note to, or move a one-off meal, by sitting ID and its current date. Repeating meals are refused. |
441
+ | `delete_meal_sitting` | destructive | Delete a one-off meal. Local server only. Previews unless `confirm: true`; reports success only when Skylight confirms the removal. |
442
+ | `get_recipes` / `get_recipe` | read | Browse saved recipes |
443
+ | `create_recipe` / `update_recipe` | write | Add or change a recipe |
444
+ | `delete_recipe` | destructive | Delete a recipe. Local server only. |
445
+ | `add_recipe_to_grocery_list` | write | Push a recipe's ingredients to the grocery list |
446
+
447
+ ### Reward Tools
448
+
449
+ Rewards need Skylight Plus.
450
+
451
+ | Tool | Effect | Description |
452
+ |------|--------|-------------|
453
+ | `get_rewards` | read | Available rewards |
454
+ | `get_reward_points` | read | Reward points balance |
455
+ | `create_reward` | write | Add a reward |
456
+ | `update_reward` | write | Change a reward |
457
+ | `redeem_reward` | write | Redeem a reward, spending points |
458
+ | `unredeem_reward` | write | Undo a redemption |
459
+ | `delete_reward` | destructive | Delete a reward. Local server only. |
460
+
461
+ ### Photo Tools
462
+
463
+ Needs Skylight Plus. Local server only.
464
+
465
+ | Tool | Effect | Description |
466
+ |------|--------|-------------|
467
+ | `get_albums` | read | Photo albums |
468
+
469
+ ## Skylight API calls used
470
+
471
+ All calls go to `https://app.ourskylight.com/api` (pinned in code). `{f}` is the frame ID. "Live" means run against a real frame by this project; everything else came from upstream and has not been re-run. Messages and meal edit/delete send `Skylight-Api-Version: 2026-10-15`, the version the web app sends; other calls use the client default.
472
+
473
+ | Function | Calls |
474
+ |----------|-------|
475
+ | Calendar | `GET/POST /frames/{f}/calendar_events` (live), `PUT /calendar_events/{id}`, `DELETE /calendar_events/{id}` (live), `GET /frames/{f}/source_calendars` (live) |
476
+ | Messages | `GET /frames/{f}/nudges?after&before`, `POST /nudges` (everyone or `category_ids`), `PATCH /nudges/{id}`, `DELETE /nudges/{id}` (all live) |
477
+ | Meals | `GET /meals/categories`, `GET/POST /meals/sittings`, `PATCH/DELETE /meals/sittings/{id}/instances/{date}` (all live) |
478
+ | Recipes | `GET/POST /meals/recipes`, `GET/PATCH/DELETE /meals/recipes/{id}`, `POST /meals/recipes/{id}/add_to_grocery_list` |
479
+ | Profiles and family | `GET /frames/{f}/categories` (live), `GET /frames/{f}`, `GET /frames/{f}/devices`, `GET /api/avatars`, `GET /api/colors` |
480
+ | Chores and tasks | `GET/POST /frames/{f}/chores`, `PUT/DELETE /chores/{id}`, `POST /frames/{f}/task_box/items` |
481
+ | Lists | `GET/POST /lists`, `GET/PUT/DELETE /lists/{id}`, `POST /lists/{id}/list_items`, `PUT/DELETE /lists/{id}/list_items/{itemId}` |
482
+ | Rewards | `GET/POST /rewards`, `PATCH/DELETE /rewards/{id}`, `POST /rewards/{id}/redeem`, `POST /rewards/{id}/unredeem`, `GET /reward_points` |
483
+ | Photos | `GET /frames/{f}/albums` (local only) |
484
+
485
+ The planning tools make no calls of their own; they combine the calendar, chore, meal and list calls above.
486
+
487
+ ## Example Queries
488
+
489
+ Once configured, you can ask Claude things like:
490
+
491
+ - "What's on my calendar today?"
492
+ - "What chores do I need to do this week?"
493
+ - "Add 'take out trash' to my chores for tomorrow"
494
+ - "What's on the grocery list?"
495
+ - "Add milk to my task list"
496
+ - "Who are the family members on Skylight?"
497
+ - "How many reward points does each person have?"
498
+
499
+ ## Development
500
+
501
+ ```bash
502
+ # Run in development mode (with hot reload)
503
+ npm run dev
504
+
505
+ # Build
506
+ npm run build
507
+
508
+ # Run tests
509
+ npm test
510
+
511
+ # Type check
512
+ npm run typecheck
513
+ ```
514
+
515
+ ### Live tests
516
+
517
+ `npm test` uses mocked network calls and is what CI runs. A separate, opt-in
518
+ suite runs the real tools against your own Skylight frame to catch drift in
519
+ Skylight's API: read smoke checks, response-shape contract checks, and a
520
+ create/edit/move/delete round trip on a throwaway meal. It does nothing unless
521
+ you set `SKYLIGHT_LIVE=1`, never sends messages, and cleans up after itself.
522
+ Each test is one file in `tests/live/`, so adding or removing one is adding or
523
+ deleting a file. See [tests/live/README.md](tests/live/README.md).
524
+
525
+ ```bash
526
+ SKYLIGHT_FRAME_ID=your_frame_id SKYLIGHT_TIMEZONE=America/Los_Angeles \
527
+ SKYLIGHT_LIVE=1 npm run test:live
528
+ ```
529
+
530
+ Set `SKYLIGHT_TIMEZONE` to your frame's zone. It defaults to `America/New_York`,
531
+ which shifts every date window the suite asks for if your frame is elsewhere.
532
+ Sign in first with `skylight-mcp auth login`.
533
+
534
+ ## API Documentation
535
+
536
+ This MCP server is built on top of the reverse-engineered Skylight API. The API endpoints were documented using the [skylight-api](https://github.com/TheEagleByte/skylight-api) project, which converts browser network traffic (HAR files) into an OpenAPI specification.
537
+
538
+ **API Resources:**
539
+ - [Interactive API Docs (Swagger UI)](https://theeaglebyte.github.io/skylight-api/swagger.html)
540
+ - [API Reference (ReDoc)](https://theeaglebyte.github.io/skylight-api/redoc.html)
541
+ - [OpenAPI Specification](https://theeaglebyte.github.io/skylight-api/openapi/openapi.yaml)
542
+
543
+ If you discover new API endpoints or find issues with the current documentation, please contribute to the [skylight-api](https://github.com/TheEagleByte/skylight-api) repository.
544
+
545
+ ## Contributing
546
+
547
+ Contributions are welcome! Here's how you can help:
548
+
549
+ 1. **Fork the repository** and create a feature branch
550
+ 2. **Make your changes** with clear, descriptive commits
551
+ 3. **Run tests** (`npm test`) and linting (`npm run lint`) before submitting
552
+ 4. **Open a pull request** with a description of your changes
553
+
554
+ ### Development Setup
555
+
556
+ ```bash
557
+ git clone https://github.com/TheEagleByte/skylight-mcp.git
558
+ cd skylight-mcp
559
+ npm install
560
+ npm run dev # Start with hot reload
561
+ ```
562
+
563
+ ### Areas for Contribution
564
+
565
+ - Adding support for new Skylight API endpoints
566
+ - Improving error handling and edge cases
567
+ - Enhancing documentation
568
+ - Writing additional tests
569
+
570
+ ## Issues & Support
571
+
572
+ - **Bug reports**: [Open an issue](https://github.com/TheEagleByte/skylight-mcp/issues/new) with steps to reproduce
573
+ - **Feature requests**: [Open an issue](https://github.com/TheEagleByte/skylight-mcp/issues/new) describing the use case
574
+ - **Questions**: [Start a discussion](https://github.com/TheEagleByte/skylight-mcp/discussions) or open an issue
575
+
576
+ Please include relevant details like your Node.js version, error messages, and configuration (with sensitive values redacted).
577
+
578
+ ## License
579
+
580
+ MIT
581
+
582
+ ## Disclaimer
583
+
584
+ This is an unofficial integration. The Skylight API is reverse-engineered and may change without notice. Use at your own risk.
@@ -0,0 +1,38 @@
1
+ # Third-party notices
2
+
3
+ ## TheEagleByte/skylight-mcp
4
+
5
+ This project is a fork of <https://github.com/TheEagleByte/skylight-mcp>, used
6
+ under the MIT License. See `LICENSE`.
7
+
8
+ ## kamilio/skylight-calendar-agent
9
+
10
+ The browser-paste OAuth sign-in flow and the Keychain-backed encrypted
11
+ credential store in `src/api/oauth.ts` and `src/api/credential-store.ts` were
12
+ designed after the corresponding parts of
13
+ <https://github.com/kamilio/skylight-calendar-agent>. No code was copied
14
+ verbatim, but the approach is theirs. That project is licensed as follows:
15
+
16
+ ```
17
+ MIT License
18
+
19
+ Copyright (c) 2026 Kamil Jopek
20
+
21
+ Permission is hereby granted, free of charge, to any person obtaining a copy
22
+ of this software and associated documentation files (the "Software"), to deal
23
+ in the Software without restriction, including without limitation the rights
24
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25
+ copies of the Software, and to permit persons to whom the Software is
26
+ furnished to do so, subject to the following conditions:
27
+
28
+ The above copyright notice and this permission notice shall be included in all
29
+ copies or substantial portions of the Software.
30
+
31
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37
+ SOFTWARE.
38
+ ```