@dalmasonto/taskflow-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.
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ import { getDb } from '../db.js';
3
+ import { errorResponse, successResponse, broadcastChange } from '../helpers.js';
4
+ // ─── exported handler functions ───────────────────────────────────────
5
+ export async function listNotifications(params) {
6
+ const db = getDb();
7
+ const limit = params.limit ?? 50;
8
+ let sql = 'SELECT * FROM notifications';
9
+ const values = [];
10
+ if (params.unread_only) {
11
+ sql += ' WHERE read = 0';
12
+ }
13
+ sql += ' ORDER BY created_at DESC LIMIT ?';
14
+ values.push(limit);
15
+ const rows = db.prepare(sql).all(...values);
16
+ return successResponse(rows);
17
+ }
18
+ export async function markNotificationRead(params) {
19
+ const db = getDb();
20
+ const result = db
21
+ .prepare('UPDATE notifications SET read = 1 WHERE id = ?')
22
+ .run(params.id);
23
+ if (result.changes === 0) {
24
+ return errorResponse('Notification not found', 'NOT_FOUND');
25
+ }
26
+ const row = db
27
+ .prepare('SELECT * FROM notifications WHERE id = ?')
28
+ .get(params.id);
29
+ const notification = row;
30
+ broadcastChange('notification', 'notification_updated', notification);
31
+ return successResponse(notification);
32
+ }
33
+ export async function markAllNotificationsRead() {
34
+ const db = getDb();
35
+ const result = db
36
+ .prepare('UPDATE notifications SET read = 1 WHERE read = 0')
37
+ .run();
38
+ broadcastChange('notification', 'notifications_all_read', {});
39
+ return successResponse({ updated: result.changes });
40
+ }
41
+ export async function clearNotifications() {
42
+ const db = getDb();
43
+ const countRow = db
44
+ .prepare('SELECT COUNT(*) AS count FROM notifications')
45
+ .get();
46
+ db.prepare('DELETE FROM notifications').run();
47
+ broadcastChange('notification', 'notifications_cleared', {});
48
+ return successResponse({ deleted: countRow.count, message: 'Notifications cleared' });
49
+ }
50
+ // ─── MCP registration ─────────────────────────────────────────────────
51
+ export function registerNotificationTools(server) {
52
+ server.tool('list_notifications', 'List notifications. Check with unread_only=true at conversation start to surface important updates for the user.', {
53
+ limit: z.number().optional(),
54
+ unread_only: z.boolean().optional(),
55
+ }, async (params) => listNotifications(params));
56
+ server.tool('mark_notification_read', 'Mark a notification as read after surfacing it to the user.', { id: z.number() }, async (params) => markNotificationRead(params));
57
+ server.tool('mark_all_notifications_read', 'Mark all unread notifications as read. Call after the user has been briefed on pending notifications.', {}, async () => markAllNotificationsRead());
58
+ server.tool('clear_notifications', 'Delete all notifications. Use with caution — this is irreversible.', {}, async () => clearNotifications());
59
+ }
@@ -0,0 +1,55 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function createProject(params: {
3
+ name: string;
4
+ color?: string;
5
+ type?: string;
6
+ description?: string;
7
+ }): Promise<{
8
+ content: {
9
+ type: "text";
10
+ text: string;
11
+ }[];
12
+ }>;
13
+ export declare function listProjects(): Promise<{
14
+ content: {
15
+ type: "text";
16
+ text: string;
17
+ }[];
18
+ }>;
19
+ export declare function getProject(params: {
20
+ id: number;
21
+ }): Promise<{
22
+ content: {
23
+ type: "text";
24
+ text: string;
25
+ }[];
26
+ }>;
27
+ export declare function updateProject(params: {
28
+ id: number;
29
+ name?: string;
30
+ color?: string;
31
+ type?: string;
32
+ description?: string;
33
+ }): Promise<{
34
+ content: {
35
+ type: "text";
36
+ text: string;
37
+ }[];
38
+ }>;
39
+ export declare function deleteProject(params: {
40
+ id: number;
41
+ }): Promise<{
42
+ content: {
43
+ type: "text";
44
+ text: string;
45
+ }[];
46
+ }>;
47
+ export declare function searchProjects(params: {
48
+ query: string;
49
+ }): Promise<{
50
+ content: {
51
+ type: "text";
52
+ text: string;
53
+ }[];
54
+ }>;
55
+ export declare function registerProjectTools(server: McpServer): void;
@@ -0,0 +1,112 @@
1
+ import { z } from 'zod';
2
+ import { getDb } from '../db.js';
3
+ import { logActivity, errorResponse, successResponse, now, broadcastChange } from '../helpers.js';
4
+ import { ProjectType } from '../types.js';
5
+ function parseTask(row) {
6
+ return {
7
+ ...row,
8
+ dependencies: JSON.parse(row.dependencies),
9
+ links: JSON.parse(row.links),
10
+ tags: JSON.parse(row.tags),
11
+ };
12
+ }
13
+ // ─── exported handler functions ───────────────────────────────────────
14
+ export async function createProject(params) {
15
+ const db = getDb();
16
+ const { name, color = '#de8eff', type = 'active_project', description, } = params;
17
+ const ts = now();
18
+ const result = db.prepare(`INSERT INTO projects (name, color, type, description, created_at, updated_at)
19
+ VALUES (?, ?, ?, ?, ?, ?)`).run(name, color, type, description ?? null, ts, ts);
20
+ const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(result.lastInsertRowid);
21
+ logActivity('project_created', name, { entityType: 'project', entityId: project.id });
22
+ const createdProject = project;
23
+ broadcastChange('project', 'project_created', createdProject);
24
+ return successResponse(createdProject);
25
+ }
26
+ export async function listProjects() {
27
+ const db = getDb();
28
+ const rows = db.prepare(`SELECT projects.*, COUNT(tasks.id) AS task_count
29
+ FROM projects
30
+ LEFT JOIN tasks ON tasks.project_id = projects.id
31
+ GROUP BY projects.id`).all();
32
+ return successResponse(rows);
33
+ }
34
+ export async function getProject(params) {
35
+ const db = getDb();
36
+ const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(params.id);
37
+ if (!project)
38
+ return errorResponse('Project not found', 'NOT_FOUND');
39
+ const tasks = db.prepare('SELECT * FROM tasks WHERE project_id = ?').all(params.id);
40
+ return successResponse({
41
+ ...project,
42
+ tasks: tasks.map(parseTask),
43
+ });
44
+ }
45
+ export async function updateProject(params) {
46
+ const db = getDb();
47
+ const existing = db.prepare('SELECT * FROM projects WHERE id = ?').get(params.id);
48
+ if (!existing)
49
+ return errorResponse('Project not found', 'NOT_FOUND');
50
+ const updates = {};
51
+ if (params.name !== undefined)
52
+ updates.name = params.name;
53
+ if (params.color !== undefined)
54
+ updates.color = params.color;
55
+ if (params.type !== undefined)
56
+ updates.type = params.type;
57
+ if (params.description !== undefined)
58
+ updates.description = params.description;
59
+ if (Object.keys(updates).length === 0) {
60
+ return successResponse(existing);
61
+ }
62
+ updates.updated_at = now();
63
+ const setClauses = Object.keys(updates).map(k => `${k} = ?`).join(', ');
64
+ const vals = Object.values(updates);
65
+ db.prepare(`UPDATE projects SET ${setClauses} WHERE id = ?`).run(...vals, params.id);
66
+ logActivity('project_updated', params.name ?? existing.name, { entityType: 'project', entityId: params.id });
67
+ const updated = db.prepare('SELECT * FROM projects WHERE id = ?').get(params.id);
68
+ const updatedProject = updated;
69
+ broadcastChange('project', 'project_updated', updatedProject);
70
+ return successResponse(updatedProject);
71
+ }
72
+ export async function deleteProject(params) {
73
+ const db = getDb();
74
+ const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(params.id);
75
+ if (!project)
76
+ return errorResponse('Project not found', 'NOT_FOUND');
77
+ // FK ON DELETE SET NULL handles unlinking tasks automatically
78
+ db.prepare('DELETE FROM projects WHERE id = ?').run(params.id);
79
+ logActivity('project_deleted', project.name, { entityType: 'project', entityId: params.id });
80
+ broadcastChange('project', 'project_deleted', { id: params.id });
81
+ return successResponse({ deleted: true, id: params.id });
82
+ }
83
+ export async function searchProjects(params) {
84
+ const db = getDb();
85
+ const like = `%${params.query}%`;
86
+ const rows = db.prepare(`SELECT projects.*, COUNT(tasks.id) AS task_count
87
+ FROM projects
88
+ LEFT JOIN tasks ON tasks.project_id = projects.id
89
+ WHERE projects.name LIKE ? COLLATE NOCASE OR projects.description LIKE ? COLLATE NOCASE
90
+ GROUP BY projects.id`).all(like, like);
91
+ return successResponse(rows);
92
+ }
93
+ // ─── MCP registration ─────────────────────────────────────────────────
94
+ export function registerProjectTools(server) {
95
+ server.tool('create_project', 'Create a new project. Projects group related tasks and track time across them.', {
96
+ name: z.string(),
97
+ color: z.string().optional(),
98
+ type: ProjectType.optional(),
99
+ description: z.string().optional(),
100
+ }, async (params) => createProject(params));
101
+ server.tool('list_projects', 'List all projects with task count. Call this at conversation start to understand the workspace.', {}, async () => listProjects());
102
+ server.tool('get_project', 'Get a project by ID with all its tasks. Use this to understand the full scope of a project before starting work.', { id: z.number() }, async (params) => getProject(params));
103
+ server.tool('update_project', 'Update project fields such as name, color, type, or description.', {
104
+ id: z.number(),
105
+ name: z.string().optional(),
106
+ color: z.string().optional(),
107
+ type: ProjectType.optional(),
108
+ description: z.string().optional(),
109
+ }, async (params) => updateProject(params));
110
+ server.tool('delete_project', 'Delete a project by ID. Tasks under this project will be unlinked (project_id set to NULL), not deleted.', { id: z.number() }, async (params) => deleteProject(params));
111
+ server.tool('search_projects', 'Search projects by name or description. Use this to find projects related to your current work.', { query: z.string() }, async (params) => searchProjects(params));
112
+ }
@@ -0,0 +1,19 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function getSetting(params: {
3
+ key: string;
4
+ }): Promise<{
5
+ content: {
6
+ type: "text";
7
+ text: string;
8
+ }[];
9
+ }>;
10
+ export declare function updateSetting(params: {
11
+ key: string;
12
+ value: unknown;
13
+ }): Promise<{
14
+ content: {
15
+ type: "text";
16
+ text: string;
17
+ }[];
18
+ }>;
19
+ export declare function registerSettingsTools(server: McpServer): void;
@@ -0,0 +1,52 @@
1
+ import { z } from 'zod';
2
+ import { getDb } from '../db.js';
3
+ import { logActivity, successResponse, broadcastChange } from '../helpers.js';
4
+ // ─── defaults ─────────────────────────────────────────────────────────
5
+ const DEFAULT_SETTINGS = {
6
+ timerBarDisplayMode: 'carousel',
7
+ notificationInterval: 30,
8
+ browserNotificationsEnabled: true,
9
+ statusColors: {
10
+ not_started: '#484847',
11
+ in_progress: '#00fbfb',
12
+ paused: '#de8eff',
13
+ blocked: '#ff6e84',
14
+ partial_done: '#ffeb3b',
15
+ done: '#69fd5d',
16
+ },
17
+ glowIntensity: 50,
18
+ backdropBlur: 8,
19
+ shadowSpread: 4,
20
+ operatorName: 'operator',
21
+ systemName: 'SYSTEM',
22
+ terminalHistory: [],
23
+ };
24
+ // ─── exported handler functions ───────────────────────────────────────
25
+ export async function getSetting(params) {
26
+ const db = getDb();
27
+ const row = db
28
+ .prepare('SELECT value FROM settings WHERE key = ?')
29
+ .get(params.key);
30
+ if (row) {
31
+ return successResponse({ key: params.key, value: JSON.parse(row.value) });
32
+ }
33
+ const defaultValue = Object.prototype.hasOwnProperty.call(DEFAULT_SETTINGS, params.key)
34
+ ? DEFAULT_SETTINGS[params.key]
35
+ : null;
36
+ return successResponse({ key: params.key, value: defaultValue });
37
+ }
38
+ export async function updateSetting(params) {
39
+ const db = getDb();
40
+ db.prepare('INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)').run(params.key, JSON.stringify(params.value));
41
+ logActivity('settings_saved', params.key, { detail: `Setting "${params.key}" updated` });
42
+ broadcastChange('setting', 'settings_saved', { key: params.key, value: params.value });
43
+ return successResponse({ key: params.key, value: params.value });
44
+ }
45
+ // ─── MCP registration ─────────────────────────────────────────────────
46
+ export function registerSettingsTools(server) {
47
+ server.tool('get_setting', 'Get a setting value by key, returning the default if not set. Available keys: timerBarDisplayMode, notificationInterval, statusColors, operatorName, systemName, and more.', { key: z.string() }, async (params) => getSetting(params));
48
+ server.tool('update_setting', 'Update or create a setting value. Settings persist across sessions in the local SQLite database.', {
49
+ key: z.string(),
50
+ value: z.unknown(),
51
+ }, async (params) => updateSetting(params));
52
+ }
@@ -0,0 +1,103 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function createTask(params: {
3
+ title: string;
4
+ description?: string;
5
+ status?: string;
6
+ priority?: string;
7
+ project_id?: number;
8
+ dependencies?: number[];
9
+ tags?: string[];
10
+ links?: Array<{
11
+ label: string;
12
+ url: string;
13
+ }>;
14
+ due_date?: string;
15
+ estimated_time?: number;
16
+ }): Promise<{
17
+ content: {
18
+ type: "text";
19
+ text: string;
20
+ }[];
21
+ }>;
22
+ export declare function listTasks(params: {
23
+ status?: string;
24
+ project_id?: number;
25
+ priority?: string;
26
+ tag?: string;
27
+ }): Promise<{
28
+ content: {
29
+ type: "text";
30
+ text: string;
31
+ }[];
32
+ }>;
33
+ export declare function getTask(params: {
34
+ id: number;
35
+ }): Promise<{
36
+ content: {
37
+ type: "text";
38
+ text: string;
39
+ }[];
40
+ }>;
41
+ export declare function updateTask(params: {
42
+ id: number;
43
+ title?: string;
44
+ description?: string;
45
+ status?: string;
46
+ priority?: string;
47
+ project_id?: number;
48
+ dependencies?: number[];
49
+ tags?: string[];
50
+ links?: Array<{
51
+ label: string;
52
+ url: string;
53
+ }>;
54
+ due_date?: string;
55
+ estimated_time?: number;
56
+ }): Promise<{
57
+ content: {
58
+ type: "text";
59
+ text: string;
60
+ }[];
61
+ }>;
62
+ export declare function updateTaskStatus(params: {
63
+ id: number;
64
+ status: string;
65
+ }): Promise<{
66
+ content: {
67
+ type: "text";
68
+ text: string;
69
+ }[];
70
+ }>;
71
+ export declare function deleteTask(params: {
72
+ id: number;
73
+ }): Promise<{
74
+ content: {
75
+ type: "text";
76
+ text: string;
77
+ }[];
78
+ }>;
79
+ export declare function bulkCreateTasks(params: {
80
+ tasks: Array<{
81
+ title: string;
82
+ description?: string;
83
+ priority?: string;
84
+ project_id?: number;
85
+ status?: string;
86
+ dependencies?: number[];
87
+ tags?: string[];
88
+ }>;
89
+ }): Promise<{
90
+ content: {
91
+ type: "text";
92
+ text: string;
93
+ }[];
94
+ }>;
95
+ export declare function searchTasks(params: {
96
+ query: string;
97
+ }): Promise<{
98
+ content: {
99
+ type: "text";
100
+ text: string;
101
+ }[];
102
+ }>;
103
+ export declare function registerTaskTools(server: McpServer): void;