@todoist/automations-mcp 0.0.0-stage → 3.0.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.
package/README.md CHANGED
@@ -1,3 +1,119 @@
1
- # Temporary Holding Version
1
+ # `@todoist/automations-mcp`
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
+ The MCP server for Todoist Automations: the tools that give an agent the same access to a user's
4
+ automations that the `tda` CLI gives, packaged for the hosted MCP server at `ai.todoist.net`. The
5
+ design and the rollout it is part of are in
6
+ [the spec](../../docs/specifications/automations-mcp-package.md).
7
+
8
+ ## What it exports
9
+
10
+ `getMcpServer({ token, baseUrl })` builds an `McpServer` over one user's Automations, bound to the
11
+ bearer token it is given: a delegated Todoist OAuth token with the `automations` audience, or a
12
+ Todoist personal token.
13
+
14
+ `validateAutomationsToken(token, baseUrl)` classifies a token before any tool runs, with one
15
+ status-only call to the automations list: `valid`, `invalid` (401) or `forbidden` (403 with an
16
+ `insufficient_scope` challenge). A 403 without that challenge counts as `valid` on purpose: it is a
17
+ refusal consent cannot lift, such as the waiting list, so each tool surfaces the backend's own
18
+ message instead of the client looping through OAuth. Any other status throws
19
+ `TokenValidationError` from `@todoist/mcp-core/host` with the status on it, which the hosted server
20
+ treats as transient for 5xx. Both functions follow the contract every Todoist MCP server shares, from
21
+ `@todoist/mcp-core/host`.
22
+
23
+ That is the whole published surface. The tools themselves are typed against
24
+ `@doist/automations-sdk`, which is not published, so they are reached through the server rather than
25
+ exported, and the build refuses to emit a declaration that would leave a consumer with an import it
26
+ cannot resolve.
27
+
28
+ ## Chat turns
29
+
30
+ `start-conversation` and `continue-conversation` each run one durable server turn inside the tool
31
+ call, the way `tda chat` does in a process: the call blocks until the agent stops, which can take
32
+ minutes, and reports liveness every ten seconds through `notifications/progress` when the client
33
+ sent a progress token, or a debug log message otherwise. Cancelling the call aborts the upstream
34
+ turn; the partial reply is persisted and `continue-conversation` resumes it. The shared logic is
35
+ `@doist/automations-sdk/chat-turn`, so the CLI and this package cannot drift on how a pending
36
+ action is read or a connection resolved.
37
+
38
+ ## Draft tests
39
+
40
+ `test-draft` starts a test run and, unless `waitMinutes` is 0, polls it until it ends or the wait
41
+ runs out, reporting progress every ten seconds the same way. A run still going at the limit keeps
42
+ running and `get-test-run` reads it later; a cancelled or failed wait names the run for the same
43
+ reason. The start, the polling and the outcome mapping are the SDK's `startDraftTest` and
44
+ `waitForDraftTest`, shared with `tda draft test`.
45
+
46
+ ## Running it locally
47
+
48
+ Everything here can run against a real account without the hosted server. `AUTOMATIONS_API_KEY` is
49
+ a Todoist personal token or the output of `tda auth token view`; `AUTOMATIONS_BASE_URL` points at
50
+ staging or a local backend and defaults to production.
51
+
52
+ ```bash
53
+ npm run build:core # once, from the monorepo root
54
+ npm run build:mcp # produces apps/mcp/dist
55
+ npm --workspace=apps/mcp run inspect # the MCP inspector over stdio
56
+ npm --workspace=apps/mcp run tool -- find-automations '{"limit": 5}'
57
+ npm --workspace=apps/mcp run tool -- start-conversation '{"message": "Draft an automation that..."}'
58
+ npm --workspace=apps/mcp run tool:list
59
+ ```
60
+
61
+ An MCP client can point at the stdio build directly, with the same environment:
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "automations": {
67
+ "command": "node",
68
+ "args": ["/path/to/automations/apps/mcp/dist/main.js"],
69
+ "env": { "AUTOMATIONS_API_KEY": "..." }
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## Releases
76
+
77
+ Versions before 3.0.0 were published as `@doist/automations-mcp`.
78
+
79
+ `@todoist/automations-mcp` is published from `main` by
80
+ [`.github/workflows/release-mcp.yml`](../../.github/workflows/release-mcp.yml), which runs
81
+ semantic-release with the configuration in [`release.config.js`](./release.config.js). The version
82
+ comes from the conventional-commit messages of the commits that count towards this package, so
83
+ nothing is bumped by hand and the repository holds no version number of its own. The same commits
84
+ make up the release notes and the Comms announcement. Releases go to the `latest` dist tag:
85
+ the consumer is the hosted server, which pins an exact version. After publishing, the workflow sends
86
+ an `automations-mcp-updated` dispatch to `todoist/todoist-ai-integrations`, whose update workflow
87
+ installs the version, runs its checks, commits and redeploys, so a release reaches `ai.todoist.net`
88
+ without waiting for a dependency PR.
89
+
90
+ The workspace libraries the package uses are not published, so `npm run build` compiles them into
91
+ `dist/` and only the packages under `dependencies` stay external. Any change here counts, except
92
+ tests, docs and `package.json` fields that don't change what gets installed. `packages/sdk`,
93
+ `packages/core` and `packages/bitmapist` are shared with the backend and the web app, so a change
94
+ confined to them counts only when its commit is scoped `mcp` or `sdk`. Any other change there still
95
+ ships, with the next release. See
96
+ [Published packages](../../docs/standards/commit-guidelines.md#published-packages) for how to split
97
+ pull requests so each change lands in the right release.
98
+
99
+ ## Adding a tool
100
+
101
+ `src/tool-registry.ts` is the single source of truth for the tool surface; the server, `run-tool`
102
+ and the token-footprint baseline derive from it, and `src/tool-registry.test.ts` fails when the
103
+ views drift.
104
+
105
+ 1. `src/utils/tool-names.ts`: add the name.
106
+ 2. `src/tools/<tool-name>.ts`: the definition, `satisfies AutomationsTool<...>`. Copy
107
+ `find-automations.ts`.
108
+ 3. `src/tool-registry.ts`: add it to the map, in the section it belongs to.
109
+ 4. `src/tools/tool-annotations.test.ts`: add its annotation expectation.
110
+ 5. `src/tools/<tool-name>.test.ts`: tests against a faked `AutomationsApi`.
111
+ 6. `src/mcp-server.ts`: add to `instructions` only when the tool needs cross-tool routing
112
+ guidance; per-tool detail belongs in its own description.
113
+
114
+ Output schemas never use `.nullable()`: nulls are stripped before the SDK validates the output, so
115
+ a nullable field is a validation failure waiting to happen. Use `.optional()` and leave the key
116
+ out. `src/tools/output-schema-nullability.test.ts` enforces it.
117
+
118
+ `src/token-footprint.test.ts` prints what the tool surface costs a model on every `tools/list`. If a
119
+ change pushes the combined fixed cost over its budget, raise the budget in the same PR and say so.