tidee-mcp 1.0.0 → 1.0.2
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.
- package/README.md +60 -0
- package/dist/index.js +61 -3
- package/package.json +2 -6
package/README.md
CHANGED
|
@@ -60,6 +60,66 @@ needs Node and a token you paste into a config file.
|
|
|
60
60
|
`TIDEE_API_URL` exists for pointing at a local server during
|
|
61
61
|
development. Leave it unset otherwise.
|
|
62
62
|
|
|
63
|
+
## Releasing
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
pnpm run mcp:release # from the repo root
|
|
67
|
+
cd packages/tidee-life-mcp && npm publish
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`mcp:release` bumps the patch version and records a fingerprint of
|
|
71
|
+
everything that determines what gets published — this package's source,
|
|
72
|
+
`tidee-life-core/mcp`, and the runtime dependencies. CI verifies that
|
|
73
|
+
record on every pipeline.
|
|
74
|
+
|
|
75
|
+
That check exists because there are two of these in the wild: the hosted
|
|
76
|
+
endpoint at `app.tidee.com/mcp`, which updates on every deploy, and this
|
|
77
|
+
package, which updates only when somebody remembers. Sharing the tool
|
|
78
|
+
definitions stops the source drifting; nothing stops the artefacts
|
|
79
|
+
drifting, and when they do it is silent — package users just get older
|
|
80
|
+
tools than URL users. Editing a tool description in
|
|
81
|
+
`tidee-life-core/mcp` is enough to cause it, and that file is nowhere
|
|
82
|
+
near this package.
|
|
83
|
+
|
|
84
|
+
Publishing stays manual: it needs credentials and a 2FA code, the
|
|
85
|
+
version is claimed forever, and unpublishing is heavily restricted after
|
|
86
|
+
72 hours.
|
|
87
|
+
|
|
88
|
+
## The MCP registry
|
|
89
|
+
|
|
90
|
+
`server.json` is the registry entry. It lists BOTH ways to connect —
|
|
91
|
+
the hosted endpoint under `remotes`, and this package under `packages` —
|
|
92
|
+
so a client can prefer whichever suits it. The hosted one needs no token;
|
|
93
|
+
this one does, which is why only the package entry declares
|
|
94
|
+
`TIDEE_API_TOKEN`.
|
|
95
|
+
|
|
96
|
+
Three things must agree, or publishing is rejected:
|
|
97
|
+
|
|
98
|
+
| | |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `server.json` → `name` | `package.json` → `mcpName` |
|
|
101
|
+
| `server.json` → `version` | `package.json` → `version` |
|
|
102
|
+
| `server.json` → `packages[0].version` | `package.json` → `version` |
|
|
103
|
+
|
|
104
|
+
`mcpName` is how the registry verifies ownership: it checks that the npm
|
|
105
|
+
package claims the same name the registry entry does, which stops anyone
|
|
106
|
+
registering a server pointing at a package they do not own. It therefore
|
|
107
|
+
has to be inside the published tarball — bump and republish before
|
|
108
|
+
publishing to the registry, not after.
|
|
109
|
+
|
|
110
|
+
The `com.tidee/` namespace needs DNS authentication (a TXT record on
|
|
111
|
+
tidee.com) rather than the GitHub flow, so that a listing carries the
|
|
112
|
+
product's own name rather than a personal account's:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
brew install mcp-publisher
|
|
116
|
+
mcp-publisher login dns --domain tidee.com
|
|
117
|
+
mcp-publisher publish
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The registry is in preview and warns about breaking changes, so check
|
|
121
|
+
the current docs at modelcontextprotocol.io/registry before running it.
|
|
122
|
+
|
|
63
123
|
## Building
|
|
64
124
|
|
|
65
125
|
```sh
|
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
|
|
245
|
-
if (
|
|
295
|
+
const created = data.created ?? [];
|
|
296
|
+
if (created.length === 0)
|
|
246
297
|
return "Nothing was created.";
|
|
247
|
-
const
|
|
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,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tidee-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"mcpName": "com.tidee/tidee",
|
|
4
5
|
"description": "MCP server for tidee — let an assistant add items to your workspace, read what is there, and mark things done.",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"mcp",
|
|
@@ -13,11 +14,6 @@
|
|
|
13
14
|
"license": "MIT",
|
|
14
15
|
"author": "tidee Ltd",
|
|
15
16
|
"homepage": "https://www.tidee.com",
|
|
16
|
-
"repository": {
|
|
17
|
-
"type": "git",
|
|
18
|
-
"url": "git+https://gitlab.com/joetidee/tidee-life.git",
|
|
19
|
-
"directory": "packages/tidee-life-mcp"
|
|
20
|
-
},
|
|
21
17
|
"bugs": {
|
|
22
18
|
"url": "https://www.tidee.com/en/contact"
|
|
23
19
|
},
|