@addai/tasks-mcp 1.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 ADDED
@@ -0,0 +1,43 @@
1
+ # @addai/tasks-mcp
2
+
3
+ MCP server for **+Ai Tasks** — create, list, complete, update, and delete tasks
4
+ from any MCP-capable AI assistant (Claude Desktop, Claude Code, entities, etc.).
5
+
6
+ ## Tools
7
+
8
+ | Tool | Description |
9
+ |------|-------------|
10
+ | `create_task` | Create a task (title, description, assignee, due date, priority, reminders, optional AiTables write-back link). |
11
+ | `list_tasks` | List tasks in the key's workspace, filterable by assignee / status / limit. |
12
+ | `get_task` | Fetch one task by id. |
13
+ | `complete_task` | Mark a task completed (triggers any AiTables write-back). |
14
+ | `update_task` | Patch title / description / due date / priority / assignee / status. |
15
+ | `delete_task` | Delete a task. |
16
+
17
+ ## Auth
18
+
19
+ The server talks to the `tasks-api` edge function at
20
+ `https://syhzpqqvrplaqdipcymw.supabase.co/functions/v1/tasks-api`.
21
+
22
+ Every request carries `Authorization: Bearer <ADDAI_API_KEY>`, where the key is a
23
+ **workspace API key** (starts `ait_`). The edge function resolves the workspace
24
+ (and, if the key is bound to an entity, that entity's profile) from the key alone
25
+ — no `workspace_id` is ever passed in the body.
26
+
27
+ Provide the key via:
28
+
29
+ - `ADDAI_API_KEY` env var (CI / explicit), or
30
+ - the OAuth browser flow (`npx -y @addai/tasks-mcp login`).
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ npx -y @addai/tasks-mcp install
36
+ ```
37
+
38
+ ## Build from source
39
+
40
+ ```bash
41
+ npm install
42
+ npm run build # tsc -> dist/
43
+ ```
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import * as os from "node:os";
5
+ import * as readline from "node:readline";
6
+ // Service endpoint is baked in at install time and at runtime — only used here
7
+ // for the API-key validation probe during install.
8
+ const DEFAULT_API_URL = "https://syhzpqqvrplaqdipcymw.supabase.co/functions/v1/tasks-api";
9
+ // --- Helpers ---
10
+ function prompt(rl, question) {
11
+ return new Promise((resolve) => {
12
+ rl.question(question, (answer) => resolve(answer.trim()));
13
+ });
14
+ }
15
+ function print(msg) {
16
+ process.stdout.write(msg + "\n");
17
+ }
18
+ const c = (code, text) => `\x1b[38;5;${code}m${text}\x1b[0m`;
19
+ const bold = (text) => `\x1b[1m${text}\x1b[0m`;
20
+ function printLogo() {
21
+ print("");
22
+ print(` ${bold(c(51, "+Ai"))} ${bold(c(135, "Tasks"))} ${c(245, "MCP Server")}`);
23
+ print(` ${c(240, "───────────────────────")}`);
24
+ print("");
25
+ }
26
+ function printError(msg) {
27
+ process.stderr.write(`Error: ${msg}\n`);
28
+ }
29
+ function expandHome(p) {
30
+ if (p.startsWith("~")) {
31
+ return path.join(os.homedir(), p.slice(1));
32
+ }
33
+ return p;
34
+ }
35
+ // --- Config detection ---
36
+ function getConfigLocations() {
37
+ const platform = os.platform();
38
+ const locations = [];
39
+ // Claude Desktop
40
+ let desktopPath;
41
+ if (platform === "darwin") {
42
+ desktopPath = expandHome("~/Library/Application Support/Claude/claude_desktop_config.json");
43
+ }
44
+ else if (platform === "win32") {
45
+ desktopPath = path.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
46
+ }
47
+ else {
48
+ desktopPath = expandHome("~/.config/Claude/claude_desktop_config.json");
49
+ }
50
+ if (fs.existsSync(desktopPath)) {
51
+ locations.push({
52
+ name: "Claude Desktop",
53
+ path: desktopPath,
54
+ key: "mcpServers",
55
+ });
56
+ }
57
+ // Claude Code (global) - uses ~/.claude.json top-level mcpServers
58
+ const codePath = expandHome("~/.claude.json");
59
+ if (fs.existsSync(codePath)) {
60
+ locations.push({
61
+ name: "Claude Code (global)",
62
+ path: codePath,
63
+ key: "mcpServers",
64
+ });
65
+ }
66
+ // Claude Code (project-level - current directory)
67
+ const projectSettingsDir = path.join(process.cwd(), ".claude");
68
+ if (fs.existsSync(projectSettingsDir)) {
69
+ const projectPath = path.join(projectSettingsDir, "settings.local.json");
70
+ locations.push({
71
+ name: "Claude Code (this project)",
72
+ path: projectPath,
73
+ key: "mcpServers",
74
+ });
75
+ }
76
+ return locations;
77
+ }
78
+ // --- API key validation ---
79
+ async function validateApiKey(apiKey, apiUrl) {
80
+ try {
81
+ // Hit an authenticated endpoint - 401 means invalid key, anything else means
82
+ // the key was accepted (valid workspace key).
83
+ const url = `${apiUrl.replace(/\/+$/, "")}/tasks?limit=1`;
84
+ const res = await fetch(url, {
85
+ method: "GET",
86
+ headers: { Authorization: `Bearer ${apiKey}` },
87
+ });
88
+ return res.status !== 401;
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
94
+ // --- Config read/write ---
95
+ function readJsonFile(filePath) {
96
+ try {
97
+ const content = fs.readFileSync(filePath, "utf-8");
98
+ return JSON.parse(content);
99
+ }
100
+ catch (err) {
101
+ if (err.code === "ENOENT") {
102
+ return {};
103
+ }
104
+ throw new Error(`Failed to read ${filePath}: ${err.message}`);
105
+ }
106
+ }
107
+ function writeJsonFile(filePath, data) {
108
+ const dir = path.dirname(filePath);
109
+ if (!fs.existsSync(dir)) {
110
+ fs.mkdirSync(dir, { recursive: true });
111
+ }
112
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
113
+ }
114
+ function updateConfig(configPath, serverKey, apiKey) {
115
+ const config = readJsonFile(configPath);
116
+ if (!config[serverKey]) {
117
+ config[serverKey] = {};
118
+ }
119
+ // Claude Code (.claude.json) needs type: 'stdio', Desktop doesn't
120
+ const isClaudeCode = configPath.endsWith(".claude.json");
121
+ // OAuth path: omit ADDAI_API_KEY entirely — the package reads cached
122
+ // tokens from ~/.addai/tasks-mcp/ on startup.
123
+ // API-key path: include ADDAI_API_KEY so the package skips OAuth.
124
+ const env = {};
125
+ if (apiKey)
126
+ env.ADDAI_API_KEY = apiKey;
127
+ config[serverKey]["addai-tasks"] = {
128
+ ...(isClaudeCode ? { type: "stdio" } : {}),
129
+ command: "npx",
130
+ args: ["-y", "@addai/tasks-mcp@latest"],
131
+ env,
132
+ };
133
+ writeJsonFile(configPath, config);
134
+ }
135
+ // --- Main ---
136
+ async function main() {
137
+ const rl = readline.createInterface({
138
+ input: process.stdin,
139
+ output: process.stdout,
140
+ });
141
+ printLogo();
142
+ try {
143
+ // 1. Choose auth method
144
+ print(` ${c(46, "1)")} ${bold("Authenticate with +Ai")} (recommended) — opens your browser, no key handling`);
145
+ print(` ${c(46, "2)")} ${bold("Use an API key")} — paste a workspace key (starts ait_)`);
146
+ print("");
147
+ let apiKey = "";
148
+ const choice = (await prompt(rl, "Pick auth method [1/2]: ")).trim();
149
+ const apiUrl = DEFAULT_API_URL;
150
+ if (choice === "2") {
151
+ // ── API key path ────────────────────────────────────────────────
152
+ print("");
153
+ while (true) {
154
+ apiKey = await prompt(rl, "Enter your +Ai API key (starts with ait_): ");
155
+ if (!apiKey) {
156
+ printError("API key is required.");
157
+ continue;
158
+ }
159
+ if (!apiKey.startsWith("ait_")) {
160
+ printError("API key must start with 'ait_'.");
161
+ continue;
162
+ }
163
+ break;
164
+ }
165
+ print("");
166
+ print("Validating API key...");
167
+ const valid = await validateApiKey(apiKey, apiUrl);
168
+ if (!valid) {
169
+ printError("Could not validate API key. Check that the key is correct.");
170
+ const continueAnyway = await prompt(rl, "Continue anyway? (y/N): ");
171
+ if (continueAnyway.toLowerCase() !== "y") {
172
+ print("Installation cancelled.");
173
+ rl.close();
174
+ process.exit(1);
175
+ }
176
+ }
177
+ else {
178
+ print("API key validated successfully.");
179
+ }
180
+ }
181
+ else {
182
+ // ── OAuth path ──────────────────────────────────────────────────
183
+ print("");
184
+ print("Opening your browser to authenticate with +Ai...");
185
+ try {
186
+ const { getOAuthAccessToken } = await import("../oauth.js");
187
+ await getOAuthAccessToken({ interactive: true });
188
+ print(` ${c(46, "✓")} Signed in. Cached at ~/.addai/tasks-mcp/`);
189
+ // We do NOT store the token in the MCP config — the runtime reads it
190
+ // from the cache. apiKey stays empty so the env block omits ADDAI_API_KEY.
191
+ }
192
+ catch (err) {
193
+ printError(`Sign-in failed: ${err?.message || err}`);
194
+ const fallback = await prompt(rl, "Switch to API key instead? (Y/n): ");
195
+ if (fallback.toLowerCase() === "n") {
196
+ print("Installation cancelled.");
197
+ rl.close();
198
+ process.exit(1);
199
+ }
200
+ while (true) {
201
+ apiKey = await prompt(rl, "Enter your +Ai API key (starts with ait_): ");
202
+ if (apiKey.startsWith("ait_"))
203
+ break;
204
+ printError("API key must start with 'ait_'.");
205
+ }
206
+ }
207
+ }
208
+ // 2. Detect config locations
209
+ print("");
210
+ const locations = getConfigLocations();
211
+ if (locations.length === 0) {
212
+ print("No Claude configuration files found.");
213
+ print("Expected locations:");
214
+ print(" - Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json");
215
+ print(" - Claude Code: ~/.claude.json");
216
+ print("");
217
+ const createNew = await prompt(rl, "Would you like to create a Claude Desktop config? (y/N): ");
218
+ if (createNew.toLowerCase() !== "y") {
219
+ print("Installation cancelled.");
220
+ rl.close();
221
+ process.exit(1);
222
+ }
223
+ const platform = os.platform();
224
+ let desktopPath;
225
+ if (platform === "darwin") {
226
+ desktopPath = expandHome("~/Library/Application Support/Claude/claude_desktop_config.json");
227
+ }
228
+ else if (platform === "win32") {
229
+ desktopPath = path.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
230
+ }
231
+ else {
232
+ desktopPath = expandHome("~/.config/Claude/claude_desktop_config.json");
233
+ }
234
+ locations.push({
235
+ name: "Claude Desktop (new)",
236
+ path: desktopPath,
237
+ key: "mcpServers",
238
+ });
239
+ }
240
+ // 3. Select config to update
241
+ let selectedLocation;
242
+ if (locations.length === 1) {
243
+ const confirm = await prompt(rl, `Update ${locations[0].name} config at ${locations[0].path}? (Y/n): `);
244
+ if (confirm.toLowerCase() === "n") {
245
+ print("Installation cancelled.");
246
+ rl.close();
247
+ process.exit(1);
248
+ }
249
+ selectedLocation = locations[0];
250
+ }
251
+ else {
252
+ print("Found multiple Claude configurations:");
253
+ locations.forEach((loc, i) => {
254
+ print(` ${i + 1}. ${loc.name} (${loc.path})`);
255
+ });
256
+ print(` ${locations.length + 1}. All of the above`);
257
+ print("");
258
+ const sel = await prompt(rl, `Select config to update (1-${locations.length + 1}): `);
259
+ const index = parseInt(sel, 10);
260
+ if (index === locations.length + 1) {
261
+ for (const loc of locations) {
262
+ print(`Updating ${loc.name}...`);
263
+ updateConfig(loc.path, loc.key, apiKey);
264
+ print(` Updated: ${loc.path}`);
265
+ }
266
+ print("");
267
+ print("All configurations updated successfully!");
268
+ print("");
269
+ print(` ${c(46, "✓")} Setup complete. The +Ai Tasks MCP server is ready to use.`);
270
+ print(` ${c(245, "Restart Claude to load the MCP server.")}`);
271
+ rl.close();
272
+ return;
273
+ }
274
+ if (isNaN(index) || index < 1 || index > locations.length) {
275
+ printError("Invalid selection.");
276
+ rl.close();
277
+ process.exit(1);
278
+ }
279
+ selectedLocation = locations[index - 1];
280
+ }
281
+ // 4. Update the config
282
+ print("");
283
+ print(`Updating ${selectedLocation.name} config...`);
284
+ updateConfig(selectedLocation.path, selectedLocation.key, apiKey);
285
+ print(`Updated: ${selectedLocation.path}`);
286
+ // 5. Done
287
+ print("");
288
+ print(` ${c(46, "✓")} Setup complete. The +Ai Tasks MCP server is ready to use.`);
289
+ if (selectedLocation.name.includes("Code")) {
290
+ print(` ${c(245, "Restart Claude Code to load the MCP server.")}`);
291
+ }
292
+ else {
293
+ print(` ${c(245, "Restart Claude Desktop to load the MCP server.")}`);
294
+ }
295
+ rl.close();
296
+ }
297
+ catch (err) {
298
+ printError(err.message || String(err));
299
+ rl.close();
300
+ process.exit(1);
301
+ }
302
+ }
303
+ main();
304
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/bin/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * HTTP client for the +Ai Tasks API edge function.
3
+ *
4
+ * Auth: every request carries `Authorization: Bearer <apiKey>` where apiKey is a
5
+ * workspace API key (starts `ait_`). The edge function resolves workspace_id (and,
6
+ * if the key is bound to an entity, that entity + its user_profile) FROM THE KEY
7
+ * ALONE — callers never pass workspace_id in the body.
8
+ */
9
+ export interface ReminderInput {
10
+ offset_value: number;
11
+ offset_unit: 'minutes' | 'hours' | 'days';
12
+ label?: string;
13
+ }
14
+ export interface TaskLinkInput {
15
+ database_id: string;
16
+ table_id: string;
17
+ record_id?: string;
18
+ record_filters?: unknown;
19
+ status_column_id: string;
20
+ status_column_type: 'select' | 'boolean';
21
+ complete_value?: unknown;
22
+ pending_value?: unknown;
23
+ revert_on_reopen?: boolean;
24
+ }
25
+ export interface CreateTaskInput {
26
+ title: string;
27
+ description?: string;
28
+ assigned_to?: string;
29
+ due_date?: string;
30
+ priority?: 'low' | 'medium' | 'high' | 'urgent';
31
+ conversation_id?: string;
32
+ reminders?: ReminderInput[];
33
+ link?: TaskLinkInput;
34
+ }
35
+ export interface ListTasksQuery {
36
+ assigned_to?: string;
37
+ status?: 'pending' | 'completed';
38
+ limit?: number;
39
+ }
40
+ export interface UpdateTaskInput {
41
+ title?: string;
42
+ description?: string;
43
+ due_date?: string;
44
+ priority?: 'low' | 'medium' | 'high' | 'urgent';
45
+ assigned_to?: string;
46
+ status?: 'pending' | 'completed';
47
+ }
48
+ export declare class TasksApiClient {
49
+ private apiKey;
50
+ private baseUrl;
51
+ constructor(apiKey: string, baseUrl?: string);
52
+ private request;
53
+ createTask(body: CreateTaskInput): Promise<unknown>;
54
+ listTasks(query?: ListTasksQuery): Promise<unknown>;
55
+ getTask(id: string): Promise<unknown>;
56
+ completeTask(id: string): Promise<unknown>;
57
+ updateTask(id: string, body: UpdateTaskInput): Promise<unknown>;
58
+ deleteTask(id: string): Promise<unknown>;
59
+ }
package/dist/client.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * HTTP client for the +Ai Tasks API edge function.
3
+ *
4
+ * Auth: every request carries `Authorization: Bearer <apiKey>` where apiKey is a
5
+ * workspace API key (starts `ait_`). The edge function resolves workspace_id (and,
6
+ * if the key is bound to an entity, that entity + its user_profile) FROM THE KEY
7
+ * ALONE — callers never pass workspace_id in the body.
8
+ */
9
+ const DEFAULT_BASE_URL = 'https://syhzpqqvrplaqdipcymw.supabase.co/functions/v1/tasks-api';
10
+ function extractErrorMessage(body, status) {
11
+ if (body && typeof body === 'object') {
12
+ const err = body.error;
13
+ if (typeof err === 'string')
14
+ return err;
15
+ if (err && typeof err === 'object') {
16
+ return err.message || err.code || JSON.stringify(err);
17
+ }
18
+ if (body.message)
19
+ return body.message;
20
+ }
21
+ return `HTTP ${status}`;
22
+ }
23
+ export class TasksApiClient {
24
+ apiKey;
25
+ baseUrl;
26
+ constructor(apiKey, baseUrl = DEFAULT_BASE_URL) {
27
+ this.apiKey = apiKey;
28
+ this.baseUrl = baseUrl.replace(/\/$/, '');
29
+ }
30
+ async request(method, path, body) {
31
+ const res = await fetch(`${this.baseUrl}${path}`, {
32
+ method,
33
+ headers: {
34
+ 'Authorization': `Bearer ${this.apiKey}`,
35
+ 'Content-Type': 'application/json',
36
+ },
37
+ body: body !== undefined ? JSON.stringify(body) : undefined,
38
+ });
39
+ if (!res.ok) {
40
+ const errBody = await res.json().catch(() => null);
41
+ throw new Error(extractErrorMessage(errBody, res.status));
42
+ }
43
+ // 204 / empty body tolerance
44
+ const text = await res.text();
45
+ if (!text)
46
+ return {};
47
+ return JSON.parse(text);
48
+ }
49
+ createTask(body) {
50
+ return this.request('POST', '/tasks', body);
51
+ }
52
+ listTasks(query = {}) {
53
+ const params = new URLSearchParams();
54
+ if (query.assigned_to)
55
+ params.set('assigned_to', query.assigned_to);
56
+ if (query.status)
57
+ params.set('status', query.status);
58
+ if (query.limit !== undefined)
59
+ params.set('limit', String(query.limit));
60
+ const qs = params.toString();
61
+ return this.request('GET', `/tasks${qs ? `?${qs}` : ''}`);
62
+ }
63
+ getTask(id) {
64
+ return this.request('GET', `/tasks/${encodeURIComponent(id)}`);
65
+ }
66
+ completeTask(id) {
67
+ return this.request('POST', `/tasks/${encodeURIComponent(id)}/complete`);
68
+ }
69
+ updateTask(id, body) {
70
+ return this.request('PATCH', `/tasks/${encodeURIComponent(id)}`, body);
71
+ }
72
+ deleteTask(id) {
73
+ return this.request('DELETE', `/tasks/${encodeURIComponent(id)}`);
74
+ }
75
+ }
76
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ // Subcommands:
3
+ // install — interactive installer
4
+ // login — force a fresh OAuth browser flow (replaces cached tokens)
5
+ // logout — clear cached OAuth tokens + client info
6
+ const subcommand = process.argv[2];
7
+ if (subcommand === 'install') {
8
+ await import('./bin/install.js');
9
+ }
10
+ else if (subcommand === 'login') {
11
+ const { getOAuthAccessToken, clearOAuthCache } = await import('./oauth.js');
12
+ await clearOAuthCache();
13
+ try {
14
+ await getOAuthAccessToken({ interactive: true });
15
+ console.log('[tasks-mcp] signed in. Tokens cached at ~/.addai/tasks-mcp/');
16
+ }
17
+ catch (err) {
18
+ console.error('[tasks-mcp] sign-in failed:', err instanceof Error ? err.message : err);
19
+ process.exit(1);
20
+ }
21
+ }
22
+ else if (subcommand === 'logout') {
23
+ const { clearOAuthCache } = await import('./oauth.js');
24
+ await clearOAuthCache();
25
+ console.log('[tasks-mcp] cleared cached OAuth credentials at ~/.addai/tasks-mcp/');
26
+ }
27
+ else {
28
+ const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
29
+ const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
30
+ const { TasksApiClient } = await import('./client.js');
31
+ const { registerTaskTools } = await import('./tools/tasks.js');
32
+ // Service endpoint is baked in. Users only configure ADDAI_API_KEY
33
+ // (legacy / CI) — OAuth is the default for interactive sessions.
34
+ const API_URL = 'https://syhzpqqvrplaqdipcymw.supabase.co/functions/v1/tasks-api';
35
+ // 1) ADDAI_API_KEY env — explicit override / CI path (a workspace key starting "ait_").
36
+ // 2) Cached OAuth tokens — silent path for returning users.
37
+ // 3) Full OAuth browser flow — first run.
38
+ let apiKey = process.env.ADDAI_API_KEY;
39
+ if (!apiKey) {
40
+ try {
41
+ const { getOAuthAccessToken } = await import('./oauth.js');
42
+ const session = await getOAuthAccessToken();
43
+ apiKey = session.accessToken;
44
+ }
45
+ catch (err) {
46
+ console.error('Authentication failed:', err instanceof Error ? err.message : err);
47
+ console.error('Either:');
48
+ console.error(' 1) Set ADDAI_API_KEY in your env (a workspace key starting "ait_"), or');
49
+ console.error(' 2) Run `npx -y @addai/tasks-mcp login` to authenticate via browser.');
50
+ process.exit(1);
51
+ }
52
+ }
53
+ const client = new TasksApiClient(apiKey, API_URL);
54
+ const server = new McpServer({ name: 'addai-tasks', version: '1.0.0' });
55
+ registerTaskTools(server, client);
56
+ const transport = new StdioServerTransport();
57
+ await server.connect(transport);
58
+ }
59
+ export {};
60
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * MCP OAuth 2.1 client using loopback PKCE.
3
+ *
4
+ * Flow:
5
+ * 1. Read cached client + tokens from ~/.addai/flows-mcp/.
6
+ * 2. If access token is fresh → return it.
7
+ * 3. If access token expired but refresh token valid → call /token with grant_type=refresh_token.
8
+ * 4. Otherwise → full auth code flow:
9
+ * a. (One-time) Register a client via /register if no client info cached.
10
+ * b. Generate PKCE verifier + challenge.
11
+ * c. Open a one-shot loopback HTTP server on a random port.
12
+ * d. Open the system browser at /authorize.
13
+ * e. User signs in via auth.add.ai, picks workspace, approves.
14
+ * f. Auth server 302s to http://127.0.0.1:<port>/callback?code=…
15
+ * g. Loopback handler captures the code, exchanges it at /token.
16
+ * h. Tokens cached; loopback server shut down.
17
+ *
18
+ * The flow is silent and non-interactive when tokens are cached and valid —
19
+ * only the first run and the refresh-failure path pop a browser.
20
+ */
21
+ export interface OAuthSession {
22
+ accessToken: string;
23
+ expiresAt: number;
24
+ scope: string;
25
+ }
26
+ /**
27
+ * Returns a valid access token, performing whichever flow is needed:
28
+ * - cached and fresh → return as-is
29
+ * - cached, expired, refresh valid → refresh, return
30
+ * - cached client only, no tokens / refresh → full browser flow
31
+ * - nothing cached → register + full browser flow
32
+ */
33
+ export declare function getOAuthAccessToken(opts?: {
34
+ interactive?: boolean;
35
+ }): Promise<OAuthSession>;
36
+ /** Drop cached credentials. Useful for `flows-mcp logout`. */
37
+ export declare function clearOAuthCache(): Promise<void>;
package/dist/oauth.js ADDED
@@ -0,0 +1,322 @@
1
+ /**
2
+ * MCP OAuth 2.1 client using loopback PKCE.
3
+ *
4
+ * Flow:
5
+ * 1. Read cached client + tokens from ~/.addai/flows-mcp/.
6
+ * 2. If access token is fresh → return it.
7
+ * 3. If access token expired but refresh token valid → call /token with grant_type=refresh_token.
8
+ * 4. Otherwise → full auth code flow:
9
+ * a. (One-time) Register a client via /register if no client info cached.
10
+ * b. Generate PKCE verifier + challenge.
11
+ * c. Open a one-shot loopback HTTP server on a random port.
12
+ * d. Open the system browser at /authorize.
13
+ * e. User signs in via auth.add.ai, picks workspace, approves.
14
+ * f. Auth server 302s to http://127.0.0.1:<port>/callback?code=…
15
+ * g. Loopback handler captures the code, exchanges it at /token.
16
+ * h. Tokens cached; loopback server shut down.
17
+ *
18
+ * The flow is silent and non-interactive when tokens are cached and valid —
19
+ * only the first run and the refresh-failure path pop a browser.
20
+ */
21
+ import * as http from 'node:http';
22
+ import * as fs from 'node:fs/promises';
23
+ import * as path from 'node:path';
24
+ import * as os from 'node:os';
25
+ import { randomBytes, createHash } from 'node:crypto';
26
+ import { spawn } from 'node:child_process';
27
+ const ISSUER = 'https://auth.add.ai/oauth';
28
+ const DEFAULT_SCOPE = 'tasks.read tasks.write';
29
+ const CONFIG_DIR = path.join(os.homedir(), '.addai', 'tasks-mcp');
30
+ const CLIENT_FILE = path.join(CONFIG_DIR, 'client.json');
31
+ const TOKENS_FILE = path.join(CONFIG_DIR, 'tokens.json');
32
+ const ACCESS_TOKEN_REFRESH_SKEW_MS = 60 * 1000; // refresh 60s before expiry
33
+ const LOOPBACK_TIMEOUT_MS = 5 * 60 * 1000; // 5 min for user to log in
34
+ // ──────────────────────────────────────────────────────────────────────────
35
+ // File-backed store (chmod 600 for both files; dir is 700)
36
+ // ──────────────────────────────────────────────────────────────────────────
37
+ async function ensureConfigDir() {
38
+ await fs.mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
39
+ }
40
+ async function readJson(file) {
41
+ try {
42
+ const text = await fs.readFile(file, 'utf-8');
43
+ return JSON.parse(text);
44
+ }
45
+ catch (err) {
46
+ if (err?.code === 'ENOENT')
47
+ return null;
48
+ throw err;
49
+ }
50
+ }
51
+ async function writeJson(file, value) {
52
+ await ensureConfigDir();
53
+ await fs.writeFile(file, JSON.stringify(value, null, 2), { mode: 0o600 });
54
+ }
55
+ // ──────────────────────────────────────────────────────────────────────────
56
+ // PKCE
57
+ // ──────────────────────────────────────────────────────────────────────────
58
+ function base64UrlEncode(buf) {
59
+ return buf.toString('base64')
60
+ .replace(/\+/g, '-')
61
+ .replace(/\//g, '_')
62
+ .replace(/=+$/, '');
63
+ }
64
+ function newCodeVerifier() {
65
+ return base64UrlEncode(randomBytes(48));
66
+ }
67
+ function challengeFromVerifier(verifier) {
68
+ return base64UrlEncode(createHash('sha256').update(verifier).digest());
69
+ }
70
+ function newState() {
71
+ return base64UrlEncode(randomBytes(16));
72
+ }
73
+ function startLoopback(expectedState) {
74
+ return new Promise((resolve, reject) => {
75
+ const server = http.createServer();
76
+ let resolveWait;
77
+ let rejectWait;
78
+ const wait = new Promise((res, rej) => { resolveWait = res; rejectWait = rej; });
79
+ server.on('request', (req, res) => {
80
+ try {
81
+ const url = new URL(req.url || '/', `http://127.0.0.1`);
82
+ if (url.pathname !== '/callback') {
83
+ res.writeHead(404).end('Not Found');
84
+ return;
85
+ }
86
+ const err = url.searchParams.get('error');
87
+ if (err) {
88
+ const desc = url.searchParams.get('error_description') ?? '';
89
+ res.writeHead(400, { 'Content-Type': 'text/html' }).end(`<html><body><h2>Sign-in failed</h2><p>${escapeHtml(err)}: ${escapeHtml(desc)}</p><p>You can close this tab.</p></body></html>`);
90
+ rejectWait(new Error(`Authorisation server returned ${err}: ${desc}`));
91
+ return;
92
+ }
93
+ const code = url.searchParams.get('code');
94
+ const state = url.searchParams.get('state');
95
+ if (!code || !state) {
96
+ res.writeHead(400).end('Missing code or state');
97
+ rejectWait(new Error('Missing code or state in callback'));
98
+ return;
99
+ }
100
+ if (state !== expectedState) {
101
+ res.writeHead(400).end('State mismatch');
102
+ rejectWait(new Error('State mismatch (possible CSRF)'));
103
+ return;
104
+ }
105
+ res.writeHead(200, { 'Content-Type': 'text/html' }).end(`<html><body style="font-family:system-ui;padding:48px;text-align:center">
106
+ <h2>You're signed in to +Ai Tasks.</h2>
107
+ <p>You can close this tab and return to your terminal.</p>
108
+ </body></html>`);
109
+ resolveWait({ code, state });
110
+ }
111
+ catch (e) {
112
+ try {
113
+ res.writeHead(500).end('Internal error');
114
+ }
115
+ catch { }
116
+ rejectWait(e);
117
+ }
118
+ });
119
+ server.on('error', (e) => reject(e));
120
+ server.listen(0, '127.0.0.1', () => {
121
+ const address = server.address();
122
+ const port = typeof address === 'object' && address ? address.port : 0;
123
+ const timeout = setTimeout(() => rejectWait(new Error(`Auth flow timed out after ${LOOPBACK_TIMEOUT_MS / 1000}s`)), LOOPBACK_TIMEOUT_MS);
124
+ const close = () => {
125
+ clearTimeout(timeout);
126
+ server.close();
127
+ };
128
+ resolve({ port, wait, close });
129
+ });
130
+ });
131
+ }
132
+ function escapeHtml(s) {
133
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
134
+ }
135
+ function openBrowser(url) {
136
+ const platform = process.platform;
137
+ const cmd = platform === 'darwin' ? 'open'
138
+ : platform === 'win32' ? 'cmd'
139
+ : 'xdg-open';
140
+ const args = platform === 'win32' ? ['/c', 'start', '""', url] : [url];
141
+ try {
142
+ const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
143
+ child.unref();
144
+ }
145
+ catch {
146
+ // If spawning the browser fails (CI, no DE), print the URL.
147
+ process.stderr.write(`\nOpen this URL in your browser to sign in:\n ${url}\n\n`);
148
+ }
149
+ }
150
+ // ──────────────────────────────────────────────────────────────────────────
151
+ // Network helpers
152
+ // ──────────────────────────────────────────────────────────────────────────
153
+ async function registerClient(redirectUris) {
154
+ const res = await fetch(`${ISSUER}/register`, {
155
+ method: 'POST',
156
+ headers: { 'Content-Type': 'application/json' },
157
+ body: JSON.stringify({
158
+ redirect_uris: redirectUris,
159
+ client_name: `Claude (${safeHostname()})`,
160
+ scope: DEFAULT_SCOPE,
161
+ }),
162
+ });
163
+ if (!res.ok) {
164
+ const text = await res.text().catch(() => '');
165
+ throw new Error(`OAuth /register failed: ${res.status} ${text.slice(0, 200)}`);
166
+ }
167
+ const body = await res.json();
168
+ return {
169
+ client_id: body.client_id,
170
+ redirect_uris: body.redirect_uris,
171
+ client_name: body.client_name,
172
+ scope: body.scope,
173
+ };
174
+ }
175
+ async function exchangeCode(opts) {
176
+ const res = await fetch(`${ISSUER}/token`, {
177
+ method: 'POST',
178
+ headers: { 'Content-Type': 'application/json' },
179
+ body: JSON.stringify({
180
+ grant_type: 'authorization_code',
181
+ code: opts.code,
182
+ redirect_uri: opts.redirectUri,
183
+ client_id: opts.clientId,
184
+ code_verifier: opts.codeVerifier,
185
+ }),
186
+ });
187
+ if (!res.ok) {
188
+ const text = await res.text().catch(() => '');
189
+ throw new Error(`OAuth /token (code) failed: ${res.status} ${text.slice(0, 200)}`);
190
+ }
191
+ const body = await res.json();
192
+ return { ...body, obtained_at: Date.now() };
193
+ }
194
+ async function refreshTokens(opts) {
195
+ const res = await fetch(`${ISSUER}/token`, {
196
+ method: 'POST',
197
+ headers: { 'Content-Type': 'application/json' },
198
+ body: JSON.stringify({
199
+ grant_type: 'refresh_token',
200
+ refresh_token: opts.refreshToken,
201
+ client_id: opts.clientId,
202
+ }),
203
+ });
204
+ if (!res.ok) {
205
+ const text = await res.text().catch(() => '');
206
+ throw new Error(`OAuth /token (refresh) failed: ${res.status} ${text.slice(0, 200)}`);
207
+ }
208
+ const body = await res.json();
209
+ return { ...body, obtained_at: Date.now() };
210
+ }
211
+ function safeHostname() {
212
+ try {
213
+ return os.hostname();
214
+ }
215
+ catch {
216
+ return 'mcp-client';
217
+ }
218
+ }
219
+ function tokenIsFresh(t) {
220
+ if (!t)
221
+ return false;
222
+ const expiresAt = t.obtained_at + t.expires_in * 1000;
223
+ return Date.now() + ACCESS_TOKEN_REFRESH_SKEW_MS < expiresAt;
224
+ }
225
+ /**
226
+ * Returns a valid access token, performing whichever flow is needed:
227
+ * - cached and fresh → return as-is
228
+ * - cached, expired, refresh valid → refresh, return
229
+ * - cached client only, no tokens / refresh → full browser flow
230
+ * - nothing cached → register + full browser flow
231
+ */
232
+ export async function getOAuthAccessToken(opts = {}) {
233
+ await ensureConfigDir();
234
+ const cachedClient = await readJson(CLIENT_FILE);
235
+ const cachedTokens = await readJson(TOKENS_FILE);
236
+ // Fresh access token — fast path.
237
+ if (cachedClient && tokenIsFresh(cachedTokens)) {
238
+ return {
239
+ accessToken: cachedTokens.access_token,
240
+ expiresAt: cachedTokens.obtained_at + cachedTokens.expires_in * 1000,
241
+ scope: cachedTokens.scope,
242
+ };
243
+ }
244
+ // Try refresh.
245
+ if (cachedClient && cachedTokens?.refresh_token) {
246
+ try {
247
+ const refreshed = await refreshTokens({
248
+ clientId: cachedClient.client_id,
249
+ refreshToken: cachedTokens.refresh_token,
250
+ });
251
+ await writeJson(TOKENS_FILE, refreshed);
252
+ return {
253
+ accessToken: refreshed.access_token,
254
+ expiresAt: refreshed.obtained_at + refreshed.expires_in * 1000,
255
+ scope: refreshed.scope,
256
+ };
257
+ }
258
+ catch (err) {
259
+ // Refresh failed — fall through to full flow.
260
+ process.stderr.write(`[tasks-mcp] refresh token rejected: ${err.message}\n`);
261
+ }
262
+ }
263
+ if (opts.interactive === false) {
264
+ throw new Error('OAuth flow required but interactive=false. Set ADDAI_API_KEY in env or run interactively once to authenticate.');
265
+ }
266
+ // Full browser flow.
267
+ const codeVerifier = newCodeVerifier();
268
+ const codeChallenge = challengeFromVerifier(codeVerifier);
269
+ const state = newState();
270
+ // We need the loopback port BEFORE we register so the redirect URI is known.
271
+ const placeholder = await startLoopback(state);
272
+ const redirectUri = `http://127.0.0.1:${placeholder.port}/callback`;
273
+ // Register (or reuse) the OAuth client.
274
+ let client = cachedClient;
275
+ const needsRegister = !client || !client.redirect_uris.some((u) => u.startsWith('http://127.0.0.1:'));
276
+ if (needsRegister) {
277
+ client = await registerClient([redirectUri]);
278
+ await writeJson(CLIENT_FILE, client);
279
+ }
280
+ else if (!client.redirect_uris.includes(redirectUri)) {
281
+ // Existing client doesn't know about this port. Re-register a new client
282
+ // entry rather than mutating the existing one (RFC 7591 simplicity).
283
+ client = await registerClient([redirectUri]);
284
+ await writeJson(CLIENT_FILE, client);
285
+ }
286
+ // Kick off the authorization request.
287
+ const authUrl = new URL(`${ISSUER}/authorize`);
288
+ authUrl.searchParams.set('response_type', 'code');
289
+ authUrl.searchParams.set('client_id', client.client_id);
290
+ authUrl.searchParams.set('redirect_uri', redirectUri);
291
+ authUrl.searchParams.set('state', state);
292
+ authUrl.searchParams.set('scope', client.scope);
293
+ authUrl.searchParams.set('code_challenge', codeChallenge);
294
+ authUrl.searchParams.set('code_challenge_method', 'S256');
295
+ process.stderr.write('[tasks-mcp] opening browser to authenticate with +Ai...\n');
296
+ openBrowser(authUrl.toString());
297
+ let callback;
298
+ try {
299
+ callback = await placeholder.wait;
300
+ }
301
+ finally {
302
+ placeholder.close();
303
+ }
304
+ const tokens = await exchangeCode({
305
+ clientId: client.client_id,
306
+ code: callback.code,
307
+ redirectUri,
308
+ codeVerifier,
309
+ });
310
+ await writeJson(TOKENS_FILE, tokens);
311
+ return {
312
+ accessToken: tokens.access_token,
313
+ expiresAt: tokens.obtained_at + tokens.expires_in * 1000,
314
+ scope: tokens.scope,
315
+ };
316
+ }
317
+ /** Drop cached credentials. Useful for `flows-mcp logout`. */
318
+ export async function clearOAuthCache() {
319
+ await fs.rm(CLIENT_FILE, { force: true });
320
+ await fs.rm(TOKENS_FILE, { force: true });
321
+ }
322
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,MAAM,MAAM,GAAG,2BAA2B,CAAC;AAC3C,MAAM,aAAa,GAAG,wBAAwB,CAAC;AAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAClE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AACzD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEzD,MAAM,4BAA4B,GAAG,EAAE,GAAG,IAAI,CAAC,CAAK,4BAA4B;AAChF,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAU,2BAA2B;AAE/E,6EAA6E;AAC7E,2DAA2D;AAC3D,6EAA6E;AAE7E,KAAK,UAAU,eAAe;IAC5B,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,KAAK,UAAU,QAAQ,CAAI,IAAY;IACrC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,EAAE,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACxC,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,KAAc;IACnD,MAAM,eAAe,EAAE,CAAC;IACxB,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5E,CAAC;AAkBD,6EAA6E;AAC7E,OAAO;AACP,6EAA6E;AAE7E,SAAS,eAAe,CAAC,GAAW;IAClC,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;SAC1B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAgB;IAC7C,OAAO,eAAe,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,QAAQ;IACf,OAAO,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC;AAWD,SAAS,aAAa,CAAC,aAAqB;IAC1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACnC,IAAI,WAAyC,CAAC;QAC9C,IAAI,UAA+B,CAAC;QACpC,MAAM,IAAI,GAAG,IAAI,OAAO,CAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,GAAG,WAAW,GAAG,GAAG,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjG,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAChC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;gBACxD,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;oBACjC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;oBACpC,OAAO;gBACT,CAAC;gBACD,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC1C,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,EAAE,CAAC;oBAC7D,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC,GAAG,CACrD,yCAAyC,UAAU,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,IAAI,CAAC,kDAAkD,CAChI,CAAC;oBACF,UAAU,CAAC,IAAI,KAAK,CAAC,iCAAiC,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;oBACvE,OAAO;gBACT,CAAC;gBACD,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC1C,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC5C,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;oBACpB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;oBAChD,UAAU,CAAC,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC,CAAC;oBAC3D,OAAO;gBACT,CAAC;gBACD,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;oBAC5B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;oBACzC,UAAU,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;oBACxD,OAAO;gBACT,CAAC;gBACD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC,GAAG,CACrD;;;yBAGe,CAChB,CAAC;gBACF,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAC/B,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBAChB,IAAI,CAAC;oBAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAA,CAAC;gBAC1D,UAAU,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YACjC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,MAAM,OAAO,GAAG,UAAU,CACxB,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,6BAA6B,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,EACvF,mBAAmB,CACpB,CAAC;YACF,MAAM,KAAK,GAAG,GAAG,EAAE;gBACjB,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC,CAAC;YACF,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACtG,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,GAAG,GAAG,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM;QACxC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK;YAC9B,CAAC,CAAC,UAAU,CAAC;IACf,MAAM,IAAI,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACvE,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;QAC5D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kDAAkD,GAAG,MAAM,CAAC,CAAC;IACpF,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,kBAAkB;AAClB,6EAA6E;AAE7E,KAAK,UAAU,cAAc,CAAC,YAAsB;IAClD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,WAAW,EAAE;QAC5C,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,aAAa,EAAE,YAAY;YAC3B,WAAW,EAAE,WAAW,YAAY,EAAE,GAAG;YACzC,KAAK,EAAE,aAAa;SACrB,CAAC;KACH,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACjF,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAK3B;IACC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,QAAQ,EAAE;QACzC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,UAAU,EAAE,oBAAoB;YAChC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,YAAY,EAAE,IAAI,CAAC,WAAW;YAC9B,SAAS,EAAE,IAAI,CAAC,QAAQ;YACxB,aAAa,EAAE,IAAI,CAAC,YAAY;SACjC,CAAC;KACH,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,OAAO,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,EAAc,CAAC;AAC1D,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,IAAgD;IAC3E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,QAAQ,EAAE;QACzC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,UAAU,EAAE,eAAe;YAC3B,aAAa,EAAE,IAAI,CAAC,YAAY;YAChC,SAAS,EAAE,IAAI,CAAC,QAAQ;SACzB,CAAC;KACH,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,OAAO,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,EAAc,CAAC;AAC1D,CAAC;AAED,SAAS,YAAY;IACnB,IAAI,CAAC;QAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,YAAY,CAAC;IAAC,CAAC;AAC9D,CAAC;AAED,SAAS,YAAY,CAAC,CAAkB;IACtC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACrB,MAAM,SAAS,GAAG,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC;IACtD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,4BAA4B,GAAG,SAAS,CAAC;AAC/D,CAAC;AAYD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAkC,EAAE;IAC5E,MAAM,eAAe,EAAE,CAAC;IACxB,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAa,WAAW,CAAC,CAAC;IAC7D,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAW,WAAW,CAAC,CAAC;IAE3D,kCAAkC;IAClC,IAAI,YAAY,IAAI,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC;QAC/C,OAAO;YACL,WAAW,EAAE,YAAY,CAAC,YAAY;YACtC,SAAS,EAAE,YAAY,CAAC,WAAW,GAAG,YAAY,CAAC,UAAU,GAAG,IAAI;YACpE,KAAK,EAAE,YAAY,CAAC,KAAK;SAC1B,CAAC;IACJ,CAAC;IAED,eAAe;IACf,IAAI,YAAY,IAAI,YAAY,EAAE,aAAa,EAAE,CAAC;QAChD,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC;gBACpC,QAAQ,EAAE,YAAY,CAAC,SAAS;gBAChC,YAAY,EAAE,YAAY,CAAC,aAAa;aACzC,CAAC,CAAC;YACH,MAAM,SAAS,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YACxC,OAAO;gBACL,WAAW,EAAE,SAAS,CAAC,YAAY;gBACnC,SAAS,EAAE,SAAS,CAAC,WAAW,GAAG,SAAS,CAAC,UAAU,GAAG,IAAI;gBAC9D,KAAK,EAAE,SAAS,CAAC,KAAK;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,8CAA8C;YAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAwC,GAAa,CAAC,OAAO,IAAI,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,gHAAgH,CACjH,CAAC;IACJ,CAAC;IAED,qBAAqB;IACrB,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IACvC,MAAM,aAAa,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IAEzB,6EAA6E;IAC7E,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,oBAAoB,WAAW,CAAC,IAAI,WAAW,CAAC;IAEpE,wCAAwC;IACxC,IAAI,MAAM,GAAG,YAAY,CAAC;IAC1B,MAAM,aAAa,GAAG,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACtG,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,GAAG,MAAM,cAAc,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAC7C,MAAM,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;SAAM,IAAI,CAAC,MAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACxD,yEAAyE;QACzE,qEAAqE;QACrE,MAAM,GAAG,MAAM,cAAc,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAC7C,MAAM,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,sCAAsC;IACtC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,MAAM,YAAY,CAAC,CAAC;IAC/C,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IAClD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,MAAO,CAAC,SAAS,CAAC,CAAC;IACzD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAO,CAAC,KAAK,CAAC,CAAC;IACjD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;IAC1D,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;IAE1D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAClF,WAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEhC,IAAI,QAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC;IACpC,CAAC;YAAS,CAAC;QACT,WAAW,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,QAAQ,EAAE,MAAO,CAAC,SAAS;QAC3B,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,WAAW;QACX,YAAY;KACb,CAAC,CAAC;IACH,MAAM,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAErC,OAAO;QACL,WAAW,EAAE,MAAM,CAAC,YAAY;QAChC,SAAS,EAAE,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI;QACxD,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC;AACJ,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { TasksApiClient } from '../client.js';
3
+ export declare function registerTaskTools(server: McpServer, client: TasksApiClient): void;
@@ -0,0 +1,133 @@
1
+ import { z } from 'zod';
2
+ const reminderSchema = z.object({
3
+ offset_value: z
4
+ .number()
5
+ .int()
6
+ .describe('How long before the due date to fire this reminder, in the chosen unit (e.g. 30).'),
7
+ offset_unit: z
8
+ .enum(['minutes', 'hours', 'days'])
9
+ .describe('Unit for offset_value. Combined with offset_value it is measured back from the due date.'),
10
+ label: z
11
+ .string()
12
+ .optional()
13
+ .describe('Optional short label shown with the reminder (e.g. "Final reminder").'),
14
+ });
15
+ const linkSchema = z
16
+ .object({
17
+ database_id: z.string().describe('AiTables database id the task is linked to.'),
18
+ table_id: z.string().describe('AiTables table id the task is linked to.'),
19
+ record_id: z
20
+ .string()
21
+ .optional()
22
+ .describe('Specific record/row id to write back to when the task completes.'),
23
+ record_filters: z
24
+ .any()
25
+ .optional()
26
+ .describe('Filters identifying the record(s) to write back to when record_id is not known.'),
27
+ status_column_id: z
28
+ .string()
29
+ .describe('Id of the column in the linked table that represents completion status.'),
30
+ status_column_type: z
31
+ .enum(['select', 'boolean'])
32
+ .describe('Type of the status column: "select" (option value) or "boolean" (checkbox).'),
33
+ complete_value: z
34
+ .any()
35
+ .optional()
36
+ .describe('Value to set the status column to when the task is completed.'),
37
+ pending_value: z
38
+ .any()
39
+ .optional()
40
+ .describe('Value the status column should hold while the task is still pending.'),
41
+ revert_on_reopen: z
42
+ .boolean()
43
+ .optional()
44
+ .describe('If true, reverts the status column back to pending_value if the task is reopened.'),
45
+ })
46
+ .describe('Optional AiTables write-back link. When the task completes, a DB trigger updates the linked record\'s status column.');
47
+ const prioritySchema = z
48
+ .enum(['low', 'medium', 'high', 'urgent'])
49
+ .describe('Task priority. One of low, medium, high, urgent.');
50
+ function ok(result) {
51
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
52
+ }
53
+ export function registerTaskTools(server, client) {
54
+ server.tool('create_task', 'Create a new task in the workspace tied to your API key. The task is assigned to the given user (a user_profiles id or an email); if you omit assigned_to it defaults to the entity bound to your key. Supports a due date, priority, optional reminders (each an offset before the due date), and an optional AiTables write-back link so completing the task updates a record\'s status column. Returns the created task row.', {
55
+ title: z.string().describe('Short title of the task (required).'),
56
+ description: z.string().optional().describe('Longer description / details of what needs doing.'),
57
+ assigned_to: z
58
+ .string()
59
+ .optional()
60
+ .describe('Who the task is for: a user_profiles id OR an email address. Defaults to the entity bound to your API key. If neither is resolvable the API returns 400.'),
61
+ due_date: z
62
+ .string()
63
+ .optional()
64
+ .describe('When the task is due, as an ISO 8601 timestamp (e.g. "2026-07-01T17:00:00Z").'),
65
+ priority: prioritySchema.optional(),
66
+ conversation_id: z
67
+ .string()
68
+ .optional()
69
+ .describe('Optional conversation id to associate the task with the chat it came from.'),
70
+ reminders: z
71
+ .array(reminderSchema)
72
+ .optional()
73
+ .describe('Optional reminders, each fired the given offset before the due date.'),
74
+ link: linkSchema.optional(),
75
+ }, async (args) => {
76
+ const result = await client.createTask(args);
77
+ return ok(result);
78
+ });
79
+ server.tool('list_tasks', 'List tasks in the workspace tied to your API key. Optionally filter by assignee (a user_profiles id or email), by status (pending or completed), and cap the number returned with limit. Returns an array of task rows.', {
80
+ assigned_to: z
81
+ .string()
82
+ .optional()
83
+ .describe('Filter to tasks assigned to this user_profiles id or email.'),
84
+ status: z
85
+ .enum(['pending', 'completed'])
86
+ .optional()
87
+ .describe('Filter by status: "pending" or "completed".'),
88
+ limit: z.number().int().positive().optional().describe('Maximum number of tasks to return.'),
89
+ }, async (args) => {
90
+ const result = await client.listTasks(args);
91
+ return ok(result);
92
+ });
93
+ server.tool('get_task', 'Fetch a single task by its id. The task must belong to the workspace tied to your API key, otherwise the API returns 404. Returns the task row.', {
94
+ id: z.string().describe('The task id to fetch.'),
95
+ }, async ({ id }) => {
96
+ const result = await client.getTask(id);
97
+ return ok(result);
98
+ });
99
+ server.tool('complete_task', 'Mark a task as completed. Sets status to completed and records who completed it. If the task has an AiTables link, a DB trigger queues the write-back to update the linked record\'s status column. Returns the updated task row.', {
100
+ id: z.string().describe('The task id to complete.'),
101
+ }, async ({ id }) => {
102
+ const result = await client.completeTask(id);
103
+ return ok(result);
104
+ });
105
+ server.tool('update_task', 'Update fields on an existing task. Pass only the fields you want to change (title, description, due_date, priority, assigned_to, or status). The task must belong to the workspace tied to your API key. Returns the updated task row.', {
106
+ id: z.string().describe('The task id to update.'),
107
+ title: z.string().optional().describe('New title.'),
108
+ description: z.string().optional().describe('New description.'),
109
+ due_date: z
110
+ .string()
111
+ .optional()
112
+ .describe('New due date as an ISO 8601 timestamp.'),
113
+ priority: prioritySchema.optional(),
114
+ assigned_to: z
115
+ .string()
116
+ .optional()
117
+ .describe('Reassign to this user_profiles id or email.'),
118
+ status: z
119
+ .enum(['pending', 'completed'])
120
+ .optional()
121
+ .describe('Set status to "pending" or "completed".'),
122
+ }, async ({ id, ...rest }) => {
123
+ const result = await client.updateTask(id, rest);
124
+ return ok(result);
125
+ });
126
+ server.tool('delete_task', 'Delete a task by its id. The task must belong to the workspace tied to your API key. Returns { ok: true } on success.', {
127
+ id: z.string().describe('The task id to delete.'),
128
+ }, async ({ id }) => {
129
+ const result = await client.deleteTask(id);
130
+ return ok(result);
131
+ });
132
+ }
133
+ //# sourceMappingURL=tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@addai/tasks-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for +Ai Tasks - create, list, complete, update, and delete tasks via AI assistants",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "tasks-mcp": "dist/index.js",
10
+ "tasks-mcp-install": "dist/bin/install.js"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch",
15
+ "start": "node dist/index.js"
16
+ },
17
+ "dependencies": {
18
+ "@modelcontextprotocol/sdk": "^1.12.1",
19
+ "zod": "^3.23.0"
20
+ },
21
+ "devDependencies": {
22
+ "typescript": "^5.7.0",
23
+ "@types/node": "^22.0.0"
24
+ },
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "keywords": [
29
+ "mcp",
30
+ "ai-tasks",
31
+ "addai",
32
+ "claude",
33
+ "model-context-protocol"
34
+ ],
35
+ "license": "MIT",
36
+ "files": [
37
+ "dist"
38
+ ]
39
+ }