2
2
3
3
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ;
4
4
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" ;
5
- import * as path from "node:path" ;
6
- import * as os from "node:os" ;
7
5
import { z } from "zod" ;
8
6
9
7
import { TaskManagerServer } from "./src/server/TaskManagerServer.js" ;
10
- import { TaskState } from "./src/types/index.js" ;
11
-
12
- const DEFAULT_PATH = path . join ( os . homedir ( ) , "Documents" , "tasks.json" ) ;
13
- const TASK_FILE_PATH = process . env . TASK_MANAGER_FILE_PATH || DEFAULT_PATH ;
8
+ import { ALL_TOOLS } from "./src/types/tools.js" ;
14
9
15
10
// Initialize the server
16
11
const server = new McpServer ( {
@@ -20,232 +15,106 @@ const server = new McpServer({
20
15
21
16
const taskManager = new TaskManagerServer ( ) ;
22
17
23
- // Define our schemas
24
- const TaskSchema = z . object ( {
25
- title : z . string ( ) ,
26
- description : z . string ( ) ,
27
- toolRecommendations : z . string ( ) . optional ( ) ,
28
- ruleRecommendations : z . string ( ) . optional ( )
29
- } ) ;
30
-
31
- type Task = z . infer < typeof TaskSchema > ;
32
-
33
- // Project tools
34
- server . tool (
35
- "list_projects" ,
36
- {
37
- state : z . enum ( [ "open" , "pending_approval" , "completed" , "all" ] ) . optional ( )
38
- } ,
39
- async ( { state } : { state ?: TaskState } ) => {
40
- const result = await taskManager . listProjects ( state ) ;
41
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
42
- }
43
- ) ;
44
-
45
- server . tool (
46
- "read_project" ,
47
- {
48
- projectId : z . string ( )
49
- } ,
50
- async ( { projectId } : { projectId : string } ) => {
51
- const result = await taskManager . openTaskDetails ( projectId ) ;
52
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
53
- }
54
- ) ;
55
-
56
- server . tool (
57
- "create_project" ,
58
- {
59
- initialPrompt : z . string ( ) ,
60
- projectPlan : z . string ( ) . optional ( ) ,
61
- tasks : z . array ( TaskSchema )
62
- } ,
63
- async ( { initialPrompt, tasks, projectPlan } : {
64
- initialPrompt : string ;
65
- tasks : Task [ ] ;
66
- projectPlan ?: string ;
67
- } ) => {
68
- const result = await taskManager . createProject ( initialPrompt , tasks , projectPlan ) ;
69
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
70
- }
71
- ) ;
72
-
73
- server . tool (
74
- "delete_project" ,
75
- {
76
- projectId : z . string ( )
77
- } ,
78
- async ( { projectId } : { projectId : string } ) => {
79
- const projectIndex = taskManager [ "data" ] . projects . findIndex (
80
- ( p ) => p . projectId === projectId
81
- ) ;
82
- if ( projectIndex === - 1 ) {
83
- return { content : [ { type : "text" , text : JSON . stringify ( { error : "Project not found" } ) } ] } ;
18
+ // Register all tools with their predefined schemas and descriptions
19
+ for ( const tool of ALL_TOOLS ) {
20
+ // Convert JSON schema properties to Zod schema
21
+ const zodSchema : { [ key : string ] : z . ZodType < any > } = { } ;
22
+
23
+ if ( tool . inputSchema . properties ) {
24
+ for ( const [ key , prop ] of Object . entries ( tool . inputSchema . properties ) ) {
25
+ if ( typeof prop === 'object' && prop !== null ) {
26
+ const schemaProp = prop as { type : string ; enum ?: string [ ] ; description ?: string } ;
27
+ if ( schemaProp . type === "string" ) {
28
+ zodSchema [ key ] = schemaProp . enum
29
+ ? z . enum ( schemaProp . enum as [ string , ...string [ ] ] )
30
+ : z . string ( ) ;
31
+ if ( ! Array . isArray ( tool . inputSchema . required ) || ! tool . inputSchema . required . includes ( key ) ) {
32
+ zodSchema [ key ] = zodSchema [ key ] . optional ( ) ;
33
+ }
34
+ }
35
+ }
84
36
}
85
-
86
- taskManager [ "data" ] . projects . splice ( projectIndex , 1 ) ;
87
- await taskManager [ "saveTasks" ] ( ) ;
88
- return { content : [ { type : "text" , text : JSON . stringify ( { message : "Project has been deleted" } ) } ] } ;
89
- }
90
- ) ;
91
-
92
- server . tool (
93
- "add_tasks_to_project" ,
94
- {
95
- projectId : z . string ( ) ,
96
- tasks : z . array ( TaskSchema )
97
- } ,
98
- async ( { projectId, tasks } : { projectId : string ; tasks : Task [ ] } ) => {
99
- const result = await taskManager . addTasksToProject ( projectId , tasks ) ;
100
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
101
- }
102
- ) ;
103
-
104
- server . tool (
105
- "finalize_project" ,
106
- {
107
- projectId : z . string ( )
108
- } ,
109
- async ( { projectId } : { projectId : string } ) => {
110
- const result = await taskManager . approveProjectCompletion ( projectId ) ;
111
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
112
- }
113
- ) ;
114
-
115
- // Task tools
116
- server . tool (
117
- "list_tasks" ,
118
- {
119
- projectId : z . string ( ) . optional ( ) ,
120
- state : z . enum ( [ "open" , "pending_approval" , "completed" , "all" ] ) . optional ( )
121
- } ,
122
- async ( { projectId, state } : { projectId ?: string ; state ?: TaskState } ) => {
123
- const result = await taskManager . listTasks ( projectId , state ) ;
124
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
125
- }
126
- ) ;
127
-
128
- server . tool (
129
- "read_task" ,
130
- {
131
- taskId : z . string ( )
132
- } ,
133
- async ( { taskId } : { taskId : string } ) => {
134
- const result = await taskManager . openTaskDetails ( taskId ) ;
135
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
136
- }
137
- ) ;
138
-
139
- server . tool (
140
- "create_task" ,
141
- {
142
- projectId : z . string ( ) ,
143
- title : z . string ( ) ,
144
- description : z . string ( ) ,
145
- toolRecommendations : z . string ( ) . optional ( ) ,
146
- ruleRecommendations : z . string ( ) . optional ( )
147
- } ,
148
- async ( { projectId, title, description, toolRecommendations, ruleRecommendations } : {
149
- projectId : string ;
150
- title : string ;
151
- description : string ;
152
- toolRecommendations ?: string ;
153
- ruleRecommendations ?: string ;
154
- } ) => {
155
- const result = await taskManager . addTasksToProject ( projectId , [
156
- { title, description, toolRecommendations, ruleRecommendations }
157
- ] ) ;
158
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
159
37
}
160
- ) ;
161
38
162
- server . tool (
163
- "update_task" ,
164
- {
165
- projectId : z . string ( ) ,
166
- taskId : z . string ( ) ,
167
- title : z . string ( ) . optional ( ) ,
168
- description : z . string ( ) . optional ( ) ,
169
- status : z . enum ( [ "not started" , "in progress" , "done" ] ) . optional ( ) ,
170
- completedDetails : z . string ( ) . optional ( ) ,
171
- toolRecommendations : z . string ( ) . optional ( ) ,
172
- ruleRecommendations : z . string ( ) . optional ( )
173
- } ,
174
- async ( { projectId, taskId, title, description, status, completedDetails, toolRecommendations, ruleRecommendations } : {
175
- projectId : string ;
176
- taskId : string ;
177
- title ?: string ;
178
- description ?: string ;
179
- status ?: "not started" | "in progress" | "done" ;
180
- completedDetails ?: string ;
181
- toolRecommendations ?: string ;
182
- ruleRecommendations ?: string ;
183
- } ) => {
184
- // Handle status changes first
185
- if ( status === "done" ) {
186
- if ( ! completedDetails ) {
187
- return { content : [ { type : "text" , text : JSON . stringify ( { error : "completedDetails is required when setting status to 'done'" } ) } ] } ;
39
+ server . tool (
40
+ tool . name ,
41
+ zodSchema ,
42
+ async ( params : any ) => {
43
+ let result ;
44
+ switch ( tool . name ) {
45
+ case "list_projects" :
46
+ result = await taskManager . listProjects ( params . state ) ;
47
+ break ;
48
+ case "read_project" :
49
+ result = await taskManager . openTaskDetails ( params . projectId ) ;
50
+ break ;
51
+ case "create_project" :
52
+ result = await taskManager . createProject ( params . initialPrompt , params . tasks , params . projectPlan ) ;
53
+ break ;
54
+ case "delete_project" :
55
+ const projectIndex = taskManager [ "data" ] . projects . findIndex (
56
+ ( p ) => p . projectId === params . projectId
57
+ ) ;
58
+ if ( projectIndex === - 1 ) {
59
+ return { content : [ { type : "text" , text : JSON . stringify ( { error : "Project not found" } ) } ] } ;
60
+ }
61
+ taskManager [ "data" ] . projects . splice ( projectIndex , 1 ) ;
62
+ await taskManager [ "saveTasks" ] ( ) ;
63
+ result = { message : "Project has been deleted" } ;
64
+ break ;
65
+ case "add_tasks_to_project" :
66
+ result = await taskManager . addTasksToProject ( params . projectId , params . tasks ) ;
67
+ break ;
68
+ case "finalize_project" :
69
+ result = await taskManager . approveProjectCompletion ( params . projectId ) ;
70
+ break ;
71
+ case "list_tasks" :
72
+ result = await taskManager . listTasks ( params . projectId , params . state ) ;
73
+ break ;
74
+ case "read_task" :
75
+ result = await taskManager . openTaskDetails ( params . taskId ) ;
76
+ break ;
77
+ case "create_task" :
78
+ result = await taskManager . addTasksToProject ( params . projectId , [ {
79
+ title : params . title ,
80
+ description : params . description ,
81
+ toolRecommendations : params . toolRecommendations ,
82
+ ruleRecommendations : params . ruleRecommendations
83
+ } ] ) ;
84
+ break ;
85
+ case "update_task" :
86
+ if ( params . status === "done" ) {
87
+ if ( ! params . completedDetails ) {
88
+ return { content : [ { type : "text" , text : JSON . stringify ( { error : "completedDetails is required when setting status to 'done'" } ) } ] } ;
89
+ }
90
+ await taskManager . markTaskDone ( params . projectId , params . taskId , params . completedDetails ) ;
91
+ }
92
+ const updates : any = { } ;
93
+ if ( params . title ) updates . title = params . title ;
94
+ if ( params . description ) updates . description = params . description ;
95
+ if ( params . toolRecommendations ) updates . toolRecommendations = params . toolRecommendations ;
96
+ if ( params . ruleRecommendations ) updates . ruleRecommendations = params . ruleRecommendations ;
97
+ if ( Object . keys ( updates ) . length > 0 ) {
98
+ result = await taskManager . updateTask ( params . projectId , params . taskId , updates ) ;
99
+ } else {
100
+ result = { message : "Task updated" } ;
101
+ }
102
+ break ;
103
+ case "delete_task" :
104
+ result = await taskManager . deleteTask ( params . projectId , params . taskId ) ;
105
+ break ;
106
+ case "approve_task" :
107
+ result = await taskManager . approveTaskCompletion ( params . projectId , params . taskId ) ;
108
+ break ;
109
+ case "get_next_task" :
110
+ result = await taskManager . getNextTask ( params . projectId ) ;
111
+ break ;
188
112
}
189
- await taskManager . markTaskDone ( projectId , taskId , completedDetails ) ;
190
- }
191
-
192
- // Update other fields if provided
193
- const updates : {
194
- title ?: string ;
195
- description ? : string ;
196
- toolRecommendations ? : string ;
197
- ruleRecommendations ? : string ;
198
- } = { } ;
199
-
200
- if ( title ) updates . title = title ;
201
- if ( description ) updates . description = description ;
202
- if ( toolRecommendations ) updates . toolRecommendations = toolRecommendations ;
203
- if ( ruleRecommendations ) updates . ruleRecommendations = ruleRecommendations ;
204
-
205
- if ( Object . keys ( updates ) . length > 0 ) {
206
- const result = await taskManager . updateTask ( projectId , taskId , updates ) ;
207
113
return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
208
114
}
209
-
210
- return { content : [ { type : "text" , text : JSON . stringify ( { message : "Task updated" } ) } ] } ;
211
- }
212
- ) ;
213
-
214
- server . tool (
215
- "delete_task" ,
216
- {
217
- projectId : z . string ( ) ,
218
- taskId : z . string ( )
219
- } ,
220
- async ( { projectId, taskId } : { projectId : string ; taskId : string } ) => {
221
- const result = await taskManager . deleteTask ( projectId , taskId ) ;
222
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
223
- }
224
- ) ;
225
-
226
- server . tool (
227
- "approve_task" ,
228
- {
229
- projectId : z . string ( ) ,
230
- taskId : z . string ( )
231
- } ,
232
- async ( { projectId, taskId } : { projectId : string ; taskId : string } ) => {
233
- const result = await taskManager . approveTaskCompletion ( projectId , taskId ) ;
234
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
235
- }
236
- ) ;
237
-
238
- server . tool (
239
- "get_next_task" ,
240
- {
241
- projectId : z . string ( )
242
- } ,
243
- async ( { projectId } : { projectId : string } ) => {
244
- const result = await taskManager . getNextTask ( projectId ) ;
245
- return { content : [ { type : "text" , text : JSON . stringify ( result ) } ] } ;
246
- }
247
- ) ;
115
+ ) ;
116
+ }
248
117
249
118
// Start the server
250
- const transport = new StdioServerTransport ( ) ;
251
- await server . connect ( transport ) ;
119
+ const transport = new StdioServerTransport ( ) ;
120
+ await server . connect ( transport ) ;
0 commit comments