tidee-mcp 1.0.1 → 1.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 +30 -5
  2. package/dist/index.js +61 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,9 +14,16 @@ you to approve it in the browser, and that is the whole setup. In Claude
14
14
  Code that is one line:
15
15
 
16
16
  ```sh
17
- claude mcp add --transport http tidee https://app.tidee.com/mcp
17
+ claude mcp add --scope user --transport http tidee https://app.tidee.com/mcp
18
18
  ```
19
19
 
20
+ `--scope user` matters: without it the server is only registered for the
21
+ directory the command ran in, and a session started anywhere else does
22
+ not see it. Then `/mcp` → tidee → Authenticate.
23
+
24
+ Full setup instructions, including Claude on the web and desktop:
25
+ [tidee.com/en/help/connect-an-assistant](https://tidee.com/en/help/connect-an-assistant/).
26
+
20
27
  **This package** — a local server over stdio, for clients that do not
21
28
  speak HTTP MCP, or when you want to point at a development server. It
22
29
  needs Node and a token you paste into a config file.
@@ -67,6 +74,9 @@ pnpm run mcp:release # from the repo root
67
74
  cd packages/tidee-life-mcp && npm publish
68
75
  ```
69
76
 
77
+ Then push. The `mcp:registry` CI job lists the new version in the MCP
78
+ registry on the next master pipeline — see below.
79
+
70
80
  `mcp:release` bumps the patch version and records a fingerprint of
71
81
  everything that determines what gets published — this package's source,
72
82
  `tidee-life-core/mcp`, and the runtime dependencies. CI verifies that
@@ -109,16 +119,31 @@ publishing to the registry, not after.
109
119
 
110
120
  The `com.tidee/` namespace needs DNS authentication (a TXT record on
111
121
  tidee.com) rather than the GitHub flow, so that a listing carries the
112
- product's own name rather than a personal account's:
122
+ product's own name rather than a personal account's.
123
+
124
+ Publishing is automated. The `mcp:registry` job runs on every master
125
+ pipeline, runs `scripts/mcp-registry-publish.mjs`, and publishes only
126
+ when the registry does not yet have the version in `server.json`. It needs
127
+ the `MCP_REGISTRY_PRIVATE_KEY` CI/CD variable: the hex Ed25519 private
128
+ key whose public half is the `v=MCPv1` TXT record on tidee.com.
129
+
130
+ Because the registry checks `mcpName` in the npm tarball, the job fails
131
+ (as a warning, not a blocked pipeline) if `server.json` is ahead of npm.
132
+ That means `npm publish` was forgotten; publish, and the next master
133
+ pipeline catches up.
134
+
135
+ To publish by hand instead:
113
136
 
114
137
  ```sh
115
138
  brew install mcp-publisher
116
- mcp-publisher login dns --domain tidee.com
139
+ mcp-publisher login dns --domain tidee.com --private-key <hex key>
117
140
  mcp-publisher publish
118
141
  ```
119
142
 
120
- The registry is in preview and warns about breaking changes, so check
121
- the current docs at modelcontextprotocol.io/registry before running it.
143
+ The registry is in preview and warns about breaking changes. If the job
144
+ starts failing on a CLI change, check the current docs at
145
+ modelcontextprotocol.io/registry and bump `MCP_PUBLISHER_VERSION` and
146
+ `MCP_PUBLISHER_SHA256` in `.gitlab-ci.yml`.
122
147
 
123
148
  ## Building
124
149
 
package/dist/index.js CHANGED
@@ -207,6 +207,52 @@ var OAUTH_REGISTRATION_WINDOW_MS = 60 * 60 * 1e3;
207
207
  // ../tidee-life-config/dist/src/index.js
208
208
  var ALLOWED_PRIORITIZABLE_TYPES = [ITEM_TYPES.todoText.name];
209
209
 
210
+ // ../tidee-life-core/dist/src/paths/index.js
211
+ var paths = {
212
+ // Phase 4 slice F2b — the OAuth consent screen. Under /app because
213
+ // approving a grant needs a signed-in user; the signed request the
214
+ // authorize endpoint issues travels in `?request=`.
215
+ authorize: () => "/app/authorize",
216
+ activateAccount: (confirmHash) => confirmHash ? `/activate-account/${confirmHash}` : "/activate-account",
217
+ workspace: () => "/app/workspace",
218
+ forgot: () => "/forgot",
219
+ forgotSuccess: () => "/forgot/success",
220
+ // Phase 3 slice S — landing page for item-share invites. The hash
221
+ // is the ItemShareInvite.inviteHash carried in the recipient's
222
+ // email; the page decides which of the three branches to render
223
+ // (unauth signup CTAs / silent claim / add-and-verify) based on
224
+ // the caller's auth state and verifiedEmails set.
225
+ shareInvite: (inviteHash) => inviteHash ? `/invite/${inviteHash}` : "/invite",
226
+ loggedInRoot: () => "/app",
227
+ /**
228
+ * Phase 4 slice M — a link straight to one item.
229
+ *
230
+ * A query parameter on the workspace rather than a route of its own,
231
+ * because the destination IS the workspace: the item is opened in
232
+ * its box, in place, exactly as clicking it from the sidebar would.
233
+ * A dedicated route would have to reproduce that, and would leave
234
+ * the user somewhere the app has no other way of reaching.
235
+ *
236
+ * Used by the confirmations external surfaces send back — Slack and
237
+ * MCP — where the thing captured is otherwise out of sight.
238
+ */
239
+ workspaceItem: (itemShareId) => itemShareId ? `/app/workspace?item=${encodeURIComponent(itemShareId)}` : "/app/workspace",
240
+ login: () => "/login",
241
+ logout: () => "/app/logout",
242
+ notLoggedInRoot: () => "/",
243
+ organiser: () => "/app/organiser",
244
+ prioritized: () => "/app/prioritized",
245
+ backlog: () => "/app/backlog",
246
+ passwordReset: ({ passwordResetHash = null } = {}) => passwordResetHash ? `/password-reset/${passwordResetHash}` : "/password-reset",
247
+ passwordResetSuccess: () => "/password-reset-success",
248
+ resendActivationLink: () => "/resend-activation-link",
249
+ resendActivationLinkSuccess: () => "/resend-activation-link-success",
250
+ shares: () => "/app/shares",
251
+ signup: (organisationInviteHash) => organisationInviteHash ? `/signup/${organisationInviteHash}` : "/signup",
252
+ signupSuccess: () => "/signup/success",
253
+ verifyEmail: (token) => token ? `/app/verify-email/${token}` : "/app/verify-email"
254
+ };
255
+
210
256
  // ../tidee-life-core/dist/src/mcp/tools.js
211
257
  var describeToolError = (failure) => {
212
258
  const { code, status, message } = failure ?? {};
@@ -225,6 +271,11 @@ var describeToolError = (failure) => {
225
271
  return `tidee returned ${status}.`;
226
272
  return message || "The request to tidee failed.";
227
273
  };
274
+ var itemUrl = (api2, itemShareId) => {
275
+ if (!api2.appBaseUrl || !itemShareId)
276
+ return null;
277
+ return `${api2.appBaseUrl.replace(/\/+$/, "")}${paths.workspaceItem(itemShareId)}`;
278
+ };
228
279
  var tool = (spec) => spec;
229
280
  var TIDEE_TOOLS = [
230
281
  tool({
@@ -241,10 +292,14 @@ var TIDEE_TOOLS = [
241
292
  },
242
293
  async run(api2, { text, parentId }) {
243
294
  const data = await api2.capture({ text, parentId });
244
- const titles = (data.created ?? []).map((c) => c.title);
245
- if (titles.length === 0)
295
+ const created = data.created ?? [];
296
+ if (created.length === 0)
246
297
  return "Nothing was created.";
247
- const quoted = titles.map((t) => `"${t}"`).join(", ");
298
+ const titles = created.map((c) => c.title);
299
+ const quoted = created.map((c) => {
300
+ const url = itemUrl(api2, c.itemShareId);
301
+ return url ? `"${c.title}" (${url})` : `"${c.title}"`;
302
+ }).join(", ");
248
303
  if (data.usedFallback) {
249
304
  return `Saved as one item: ${quoted}. tidee could not split that into separate items, so it kept the text as given.`;
250
305
  }
@@ -372,6 +427,9 @@ var call = async (path, init) => {
372
427
  return body;
373
428
  };
374
429
  var api = {
430
+ // The API host and the app host are the same deployment, so the
431
+ // token's server is also where its items can be looked at.
432
+ appBaseUrl: API_URL,
375
433
  capture: ({ text, parentId }) => call("/api/v1/capture", {
376
434
  method: "POST",
377
435
  body: { text, parentId }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tidee-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "mcpName": "com.tidee/tidee",
5
5
  "description": "MCP server for tidee — let an assistant add items to your workspace, read what is there, and mark things done.",
6
6
  "keywords": [