@doist/automations-mcp 0.0.0 → 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.
- package/README.md +104 -45
- package/dist/chunk-BU6WJ7QM.js +7932 -0
- package/dist/chunk-BU6WJ7QM.js.map +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +7 -0
- package/dist/main.js +23 -0
- package/dist/main.js.map +7 -0
- package/dist/mcp-server.d.ts +10 -0
- package/dist/validate-token.d.ts +33 -0
- package/package.json +72 -8
package/README.md
CHANGED
|
@@ -1,45 +1,104 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
##
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
1
|
+
# `@doist/automations-mcp`
|
|
2
|
+
|
|
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
|
+
`AutomationsTokenValidationError` with the status on it, which the hosted server treats as
|
|
20
|
+
transient for 5xx.
|
|
21
|
+
|
|
22
|
+
That is the whole published surface. The tools themselves are typed against
|
|
23
|
+
`@doist/automations-sdk`, which is not published, so they are reached through the server rather than
|
|
24
|
+
exported, and the build refuses to emit a declaration that would leave a consumer with an import it
|
|
25
|
+
cannot resolve.
|
|
26
|
+
|
|
27
|
+
## Chat turns
|
|
28
|
+
|
|
29
|
+
`start-conversation` and `continue-conversation` each run one durable server turn inside the tool
|
|
30
|
+
call, the way `tda chat` does in a process: the call blocks until the agent stops, which can take
|
|
31
|
+
minutes, and reports liveness every ten seconds through `notifications/progress` when the client
|
|
32
|
+
sent a progress token, or a debug log message otherwise. Cancelling the call aborts the upstream
|
|
33
|
+
turn; the partial reply is persisted and `continue-conversation` resumes it. The shared logic is
|
|
34
|
+
`@doist/automations-sdk/chat-turn`, so the CLI and this package cannot drift on how a pending
|
|
35
|
+
action is read or a connection resolved.
|
|
36
|
+
|
|
37
|
+
## Running it locally
|
|
38
|
+
|
|
39
|
+
Everything here can run against a real account without the hosted server. `AUTOMATIONS_API_KEY` is
|
|
40
|
+
a Todoist personal token or the output of `tda auth token view`; `AUTOMATIONS_BASE_URL` points at
|
|
41
|
+
staging or a local backend and defaults to production.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run build:core # once, from the monorepo root
|
|
45
|
+
npm run build:mcp # produces apps/mcp/dist
|
|
46
|
+
npm --workspace=apps/mcp run inspect # the MCP inspector over stdio
|
|
47
|
+
npm --workspace=apps/mcp run tool -- find-automations '{"limit": 5}'
|
|
48
|
+
npm --workspace=apps/mcp run tool -- start-conversation '{"message": "Draft an automation that..."}'
|
|
49
|
+
npm --workspace=apps/mcp run tool:list
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
An MCP client can point at the stdio build directly, with the same environment:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"mcpServers": {
|
|
57
|
+
"automations": {
|
|
58
|
+
"command": "node",
|
|
59
|
+
"args": ["/path/to/automations/apps/mcp/dist/main.js"],
|
|
60
|
+
"env": { "AUTOMATIONS_API_KEY": "..." }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Releases
|
|
67
|
+
|
|
68
|
+
`@doist/automations-mcp` is published from `main` by
|
|
69
|
+
[`.github/workflows/release-mcp.yml`](../../.github/workflows/release-mcp.yml), which runs
|
|
70
|
+
semantic-release with the configuration in [`release.config.js`](./release.config.js). The version
|
|
71
|
+
comes from the conventional-commit messages that touched this directory, so nothing is bumped by
|
|
72
|
+
hand and the repository holds no version number of its own. Releases go to the `latest` dist tag:
|
|
73
|
+
the consumer is the hosted server, which pins an exact version. After publishing, the workflow sends
|
|
74
|
+
an `automations-mcp-updated` dispatch to `Doist/todoist-ai-integrations`, whose update workflow
|
|
75
|
+
installs the version, runs its checks, commits and redeploys, so a release reaches `ai.todoist.net`
|
|
76
|
+
without waiting for a dependency PR.
|
|
77
|
+
|
|
78
|
+
The workspace libraries the package uses are not published, so `npm run build` compiles them into
|
|
79
|
+
`dist/` and only the packages under `dependencies` stay external. Because a release is scoped to
|
|
80
|
+
commits under `apps/mcp`, a change confined to one of those libraries does not trigger a release
|
|
81
|
+
even though it changes what gets published; it ships with the next release instead. Publish it
|
|
82
|
+
sooner by landing it together with a change here.
|
|
83
|
+
|
|
84
|
+
## Adding a tool
|
|
85
|
+
|
|
86
|
+
`src/tool-registry.ts` is the single source of truth for the tool surface; the server, `run-tool`
|
|
87
|
+
and the token-footprint baseline derive from it, and `src/tool-registry.test.ts` fails when the
|
|
88
|
+
views drift.
|
|
89
|
+
|
|
90
|
+
1. `src/utils/tool-names.ts`: add the name.
|
|
91
|
+
2. `src/tools/<tool-name>.ts`: the definition, `satisfies AutomationsTool<...>`. Copy
|
|
92
|
+
`find-automations.ts`.
|
|
93
|
+
3. `src/tool-registry.ts`: add it to the map, in the section it belongs to.
|
|
94
|
+
4. `src/tools/tool-annotations.test.ts`: add its annotation expectation.
|
|
95
|
+
5. `src/tools/<tool-name>.test.ts`: tests against a faked `AutomationsApi`.
|
|
96
|
+
6. `src/mcp-server.ts`: add to `instructions` only when the tool needs cross-tool routing
|
|
97
|
+
guidance; per-tool detail belongs in its own description.
|
|
98
|
+
|
|
99
|
+
Output schemas never use `.nullable()`: nulls are stripped before the SDK validates the output, so
|
|
100
|
+
a nullable field is a validation failure waiting to happen. Use `.optional()` and leave the key
|
|
101
|
+
out. `src/tools/output-schema-nullability.test.ts` enforces it.
|
|
102
|
+
|
|
103
|
+
`src/token-footprint.test.ts` prints what the tool surface costs a model on every `tools/list`. If a
|
|
104
|
+
change pushes the combined fixed cost over its budget, raise the budget in the same PR and say so.
|