Skip to content

Commit 2903e89

Browse files
Fix entrypoint issue
1 parent 8f0e800 commit 2903e89

File tree

6 files changed

+217
-395
lines changed

6 files changed

+217
-395
lines changed

README.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,36 @@ npm install -g @chriscarrollsmith/mcp-taskmanager
7676

7777
## Usage
7878

79+
This command will start the MCP server:
80+
7981
```bash
80-
npm start
82+
npx -y @chriscarrollsmith/mcp-taskmanager
83+
84+
# Or, with a custom tasks.json path:
85+
TASK_MANAGER_FILE_PATH=/path/to/tasks.json npx -y @chriscarrollsmith/mcp-taskmanager
8186
```
8287

83-
You can also set a custom path for the tasks file:
88+
However, usually you will set the tool configuration in Claude Desktop, Cursor, or another MCP client as follows:
8489

85-
```bash
86-
TASK_MANAGER_FILE_PATH=/path/to/tasks.json npm start
90+
```json
91+
{
92+
"tools": {
93+
"taskmanager": {
94+
"command": "npx",
95+
"args": ["-y", "@chriscarrollsmith/mcp-taskmanager"]
96+
}
97+
}
98+
}
8799
```
88100

89-
## Development
101+
To use the CLI utility, you can use the following command:
90102

91103
```bash
92-
npm run dev
104+
npx task-manager-cli --help
93105
```
94106

107+
This will show the available commands and options.
108+
95109
## License
96110

97111
MIT

index.ts

Lines changed: 186 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { z } from "zod";
1212

1313
import { TaskManagerServer } from "./src/server/TaskManagerServer.js";
1414
import { ALL_TOOLS } from "./src/types/tools.js";
15-
import { ProjectActionSchema, TaskActionSchema } from "./src/types/index.js";
1615

1716
const DEFAULT_PATH = path.join(os.homedir(), "Documents", "tasks.json");
1817
const TASK_FILE_PATH = process.env.TASK_MANAGER_FILE_PATH || DEFAULT_PATH;
@@ -37,85 +36,217 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
3736
tools: ALL_TOOLS,
3837
}));
3938

40-
// Create specific schemas for CallToolRequestSchema to validate against our tool schemas
41-
const ProjectToolCallSchema = CallToolRequestSchema.extend({
42-
params: z.object({
43-
name: z.literal("project"),
44-
arguments: z.object({
45-
action: z.string(),
46-
arguments: z.any()
47-
})
48-
})
49-
});
50-
51-
const TaskToolCallSchema = CallToolRequestSchema.extend({
52-
params: z.object({
53-
name: z.literal("task"),
54-
arguments: z.object({
55-
action: z.string(),
56-
arguments: z.any()
57-
})
58-
})
59-
});
60-
6139
server.setRequestHandler(CallToolRequestSchema, async (request) => {
6240
try {
63-
const { name, arguments: args } = request.params;
41+
const { name } = request.params;
42+
const args = request.params.arguments || {};
43+
44+
// For validation, ensure args is an object when expected
45+
if (name !== "list_projects" && name !== "list_tasks" && Object.keys(args).length === 0) {
46+
throw new Error("Invalid arguments: expected object with parameters");
47+
}
6448

6549
switch (name) {
66-
case "project": {
67-
// Validate request against the schema
68-
const validationResult = ProjectToolCallSchema.safeParse(request);
69-
if (!validationResult.success) {
70-
throw new Error(`Invalid request: ${validationResult.error.message}`);
50+
// Project tools
51+
case "list_projects": {
52+
const result = await taskManagerServer.listProjects();
53+
return {
54+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
55+
};
56+
}
57+
58+
case "read_project": {
59+
const projectId = String(args.projectId);
60+
if (!projectId) {
61+
throw new Error("Missing required parameter: projectId");
7162
}
63+
const result = await taskManagerServer.getNextTask(projectId);
64+
return {
65+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
66+
};
67+
}
7268

73-
// Further validate the action and arguments
74-
if (!args || typeof args !== 'object') {
75-
throw new Error("Invalid arguments: expected object");
69+
case "create_project": {
70+
const initialPrompt = String(args.initialPrompt || "");
71+
if (!initialPrompt || !args.tasks || !Array.isArray(args.tasks)) {
72+
throw new Error("Missing required parameters: initialPrompt and/or tasks");
7673
}
74+
const projectPlan = args.projectPlan ? String(args.projectPlan) : undefined;
75+
76+
const result = await taskManagerServer.createProject(
77+
initialPrompt,
78+
args.tasks,
79+
projectPlan
80+
);
81+
return {
82+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
83+
};
84+
}
7785

78-
const { action, arguments: actionArgs } = args;
79-
if (!action || typeof action !== 'string') {
80-
throw new Error("Missing or invalid 'action' field");
86+
case "delete_project": {
87+
const projectId = String(args.projectId);
88+
if (!projectId) {
89+
throw new Error("Missing required parameter: projectId");
90+
}
91+
// Use the private data and saveTasks via indexing since there's no explicit delete method
92+
const projectIndex = taskManagerServer["data"].projects.findIndex((p) => p.projectId === projectId);
93+
if (projectIndex === -1) {
94+
return {
95+
content: [{ type: "text", text: JSON.stringify({ status: "error", message: "Project not found" }, null, 2) }],
96+
};
8197
}
98+
99+
taskManagerServer["data"].projects.splice(projectIndex, 1);
100+
await taskManagerServer["saveTasks"]();
101+
return {
102+
content: [{ type: "text", text: JSON.stringify({
103+
status: "project_deleted",
104+
message: `Project ${projectId} has been deleted.`
105+
}, null, 2) }],
106+
};
107+
}
82108

83-
// Validate against the specific action schema
84-
const actionSchema = ProjectActionSchema.safeParse({ action, arguments: actionArgs });
85-
if (!actionSchema.success) {
86-
throw new Error(`Invalid action parameters: ${actionSchema.error.message}`);
109+
case "add_tasks_to_project": {
110+
const projectId = String(args.projectId);
111+
if (!projectId || !args.tasks || !Array.isArray(args.tasks)) {
112+
throw new Error("Missing required parameters: projectId and/or tasks");
87113
}
114+
const result = await taskManagerServer.addTasksToProject(projectId, args.tasks);
115+
return {
116+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
117+
};
118+
}
88119

89-
const result = await taskManagerServer.handleProjectTool(action, actionArgs);
120+
case "finalize_project": {
121+
const projectId = String(args.projectId);
122+
if (!projectId) {
123+
throw new Error("Missing required parameter: projectId");
124+
}
125+
const result = await taskManagerServer.approveProjectCompletion(projectId);
90126
return {
91127
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
92128
};
93129
}
94130

95-
case "task": {
96-
// Validate request against the schema
97-
const validationResult = TaskToolCallSchema.safeParse(request);
98-
if (!validationResult.success) {
99-
throw new Error(`Invalid request: ${validationResult.error.message}`);
131+
// Task tools
132+
case "list_tasks": {
133+
// No explicit list tasks method, so return a message
134+
return {
135+
content: [{ type: "text", text: JSON.stringify({
136+
status: "error",
137+
message: "list_tasks functionality to be implemented in future version"
138+
}, null, 2) }],
139+
};
140+
}
141+
142+
case "read_task": {
143+
const taskId = String(args.taskId);
144+
if (!taskId) {
145+
throw new Error("Missing required parameter: taskId");
100146
}
147+
const result = await taskManagerServer.openTaskDetails(taskId);
148+
return {
149+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
150+
};
151+
}
101152

102-
// Further validate the action and arguments
103-
if (!args || typeof args !== 'object') {
104-
throw new Error("Invalid arguments: expected object");
153+
case "create_task": {
154+
const projectId = String(args.projectId);
155+
const title = String(args.title || "");
156+
const description = String(args.description || "");
157+
158+
if (!projectId || !title || !description) {
159+
throw new Error("Missing required parameters: projectId, title, and/or description");
105160
}
161+
162+
const result = await taskManagerServer.addTasksToProject(projectId, [{
163+
title,
164+
description
165+
}]);
166+
return {
167+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
168+
};
169+
}
106170

107-
const { action, arguments: actionArgs } = args;
108-
if (!action || typeof action !== 'string') {
109-
throw new Error("Missing or invalid 'action' field");
171+
case "update_task": {
172+
const projectId = String(args.projectId);
173+
const taskId = String(args.taskId);
174+
175+
if (!projectId || !taskId) {
176+
throw new Error("Missing required parameters: projectId and/or taskId");
110177
}
178+
179+
const updates: { title?: string; description?: string } = {};
180+
if (args.title !== undefined) updates.title = String(args.title);
181+
if (args.description !== undefined) updates.description = String(args.description);
182+
183+
const result = await taskManagerServer.updateTask(projectId, taskId, updates);
184+
185+
// Handle status change separately if needed
186+
if (args.status) {
187+
const status = args.status as "not started" | "in progress" | "done";
188+
const proj = taskManagerServer["data"].projects.find(p => p.projectId === projectId);
189+
if (proj) {
190+
const task = proj.tasks.find(t => t.id === taskId);
191+
if (task) {
192+
if (status === "done") {
193+
if (!args.completedDetails) {
194+
return {
195+
content: [{ type: "text", text: JSON.stringify({
196+
status: "error",
197+
message: "completedDetails is required when setting status to 'done'"
198+
}, null, 2) }],
199+
};
200+
}
201+
202+
// Use markTaskDone for proper transition to done status
203+
await taskManagerServer.markTaskDone(projectId, taskId, String(args.completedDetails));
204+
} else {
205+
// For other status changes
206+
task.status = status;
207+
await taskManagerServer["saveTasks"]();
208+
}
209+
}
210+
}
211+
}
212+
213+
return {
214+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
215+
};
216+
}
111217

112-
// Validate against the specific action schema
113-
const actionSchema = TaskActionSchema.safeParse({ action, arguments: actionArgs });
114-
if (!actionSchema.success) {
115-
throw new Error(`Invalid action parameters: ${actionSchema.error.message}`);
218+
case "delete_task": {
219+
const projectId = String(args.projectId);
220+
const taskId = String(args.taskId);
221+
222+
if (!projectId || !taskId) {
223+
throw new Error("Missing required parameters: projectId and/or taskId");
116224
}
225+
const result = await taskManagerServer.deleteTask(projectId, taskId);
226+
return {
227+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
228+
};
229+
}
117230

118-
const result = await taskManagerServer.handleTaskTool(action, actionArgs);
231+
case "approve_task": {
232+
const projectId = String(args.projectId);
233+
const taskId = String(args.taskId);
234+
235+
if (!projectId || !taskId) {
236+
throw new Error("Missing required parameters: projectId and/or taskId");
237+
}
238+
const result = await taskManagerServer.approveTaskCompletion(projectId, taskId);
239+
return {
240+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
241+
};
242+
}
243+
244+
case "get_next_task": {
245+
const projectId = String(args.projectId);
246+
if (!projectId) {
247+
throw new Error("Missing required parameter: projectId");
248+
}
249+
const result = await taskManagerServer.getNextTask(projectId);
119250
return {
120251
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
121252
};

package-lock.json

Lines changed: 6 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "@chriscarrollsmith/mcp-taskmanager",
3-
"version": "1.0.1",
3+
"version": "1.0.4",
44
"description": "Task Manager MCP Server",
55
"author": "Christopher C. Smith ([email protected])",
66
"main": "dist/index.js",
77
"type": "module",
88
"bin": {
99
"mcp-taskmanager": "dist/index.js",
10-
"task-manager-cli": "dist/cli.js"
10+
"task-manager-cli": "dist/src/cli.js"
1111
},
1212
"files": [
1313
"dist/index.js",
@@ -20,8 +20,8 @@
2020
"start": "node dist/index.js",
2121
"dev": "tsc && node dist/index.js",
2222
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
23-
"approve-task": "node dist/cli.js approve-task",
24-
"list-tasks": "node dist/cli.js list"
23+
"approve-task": "node dist/src/cli.js approve-task",
24+
"list-tasks": "node dist/src/cli.js list"
2525
},
2626
"repository": {
2727
"type": "git",

0 commit comments

Comments
 (0)