1use crate::events::EventManager;
11use crate::project::ProjectContext;
12use crate::report::ReportManager;
13use crate::tasks::TaskManager;
14use crate::workspace::WorkspaceManager;
15use serde::{Deserialize, Serialize};
16use serde_json::{json, Value};
17use std::io::{self, BufRead, Write};
18
19#[derive(Debug, Deserialize)]
20struct JsonRpcRequest {
21 jsonrpc: String,
22 id: Option<Value>,
23 method: String,
24 params: Option<Value>,
25}
26
27#[derive(Debug, Serialize)]
28struct JsonRpcResponse {
29 jsonrpc: String,
30 id: Option<Value>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 result: Option<Value>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 error: Option<JsonRpcError>,
35}
36
37#[derive(Debug, Serialize)]
38struct JsonRpcError {
39 code: i32,
40 message: String,
41}
42
43#[derive(Debug, Deserialize)]
44struct ToolCallParams {
45 name: String,
46 arguments: Value,
47}
48
49const MCP_TOOLS: &str = include_str!("../../mcp-server.json");
51
52pub async fn run() -> io::Result<()> {
55 run_server().await
56}
57
58async fn run_server() -> io::Result<()> {
59 let stdin = io::stdin();
60 let mut stdout = io::stdout();
61 let reader = stdin.lock();
62
63 for line in reader.lines() {
64 let line = line?;
65 if line.trim().is_empty() {
66 continue;
67 }
68
69 let response = match serde_json::from_str::<JsonRpcRequest>(&line) {
70 Ok(request) => handle_request(request).await,
71 Err(e) => JsonRpcResponse {
72 jsonrpc: "2.0".to_string(),
73 id: None,
74 result: None,
75 error: Some(JsonRpcError {
76 code: -32700,
77 message: format!("Parse error: {}", e),
78 }),
79 },
80 };
81
82 let response_json = serde_json::to_string(&response)?;
83 writeln!(stdout, "{}", response_json)?;
84 stdout.flush()?;
85 }
86
87 Ok(())
88}
89
90async fn handle_request(request: JsonRpcRequest) -> JsonRpcResponse {
91 if request.jsonrpc != "2.0" {
93 return JsonRpcResponse {
94 jsonrpc: "2.0".to_string(),
95 id: request.id,
96 result: None,
97 error: Some(JsonRpcError {
98 code: -32600,
99 message: format!("Invalid JSON-RPC version: {}", request.jsonrpc),
100 }),
101 };
102 }
103
104 let result = match request.method.as_str() {
105 "tools/list" => handle_tools_list(),
106 "tools/call" => handle_tool_call(request.params).await,
107 _ => Err(format!("Unknown method: {}", request.method)),
108 };
109
110 match result {
111 Ok(value) => JsonRpcResponse {
112 jsonrpc: "2.0".to_string(),
113 id: request.id,
114 result: Some(value),
115 error: None,
116 },
117 Err(message) => JsonRpcResponse {
118 jsonrpc: "2.0".to_string(),
119 id: request.id,
120 result: None,
121 error: Some(JsonRpcError {
122 code: -32000,
123 message,
124 }),
125 },
126 }
127}
128
129fn handle_tools_list() -> Result<Value, String> {
130 let config: Value = serde_json::from_str(MCP_TOOLS)
131 .map_err(|e| format!("Failed to parse MCP tools schema: {}", e))?;
132
133 Ok(json!({
134 "tools": config.get("tools").unwrap_or(&json!([]))
135 }))
136}
137
138async fn handle_tool_call(params: Option<Value>) -> Result<Value, String> {
139 let params: ToolCallParams = serde_json::from_value(params.unwrap_or(json!({})))
140 .map_err(|e| format!("Invalid tool call parameters: {}", e))?;
141
142 let result = match params.name.as_str() {
143 "task_add" => handle_task_add(params.arguments).await,
144 "task_start" => handle_task_start(params.arguments).await,
145 "task_pick_next" => handle_task_pick_next(params.arguments).await,
146 "task_spawn_subtask" => handle_task_spawn_subtask(params.arguments).await,
147 "task_switch" => handle_task_switch(params.arguments).await,
148 "task_done" => handle_task_done(params.arguments).await,
149 "task_update" => handle_task_update(params.arguments).await,
150 "task_find" => handle_task_find(params.arguments).await,
151 "task_get" => handle_task_get(params.arguments).await,
152 "event_add" => handle_event_add(params.arguments).await,
153 "event_list" => handle_event_list(params.arguments).await,
154 "current_task_get" => handle_current_task_get(params.arguments).await,
155 "report_generate" => handle_report_generate(params.arguments).await,
156 _ => Err(format!("Unknown tool: {}", params.name)),
157 }?;
158
159 Ok(json!({
160 "content": [{
161 "type": "text",
162 "text": serde_json::to_string_pretty(&result)
163 .unwrap_or_else(|_| "{}".to_string())
164 }]
165 }))
166}
167
168async fn handle_task_add(args: Value) -> Result<Value, String> {
171 let name = args
172 .get("name")
173 .and_then(|v| v.as_str())
174 .ok_or("Missing required parameter: name")?;
175
176 let spec = args.get("spec").and_then(|v| v.as_str());
177 let parent_id = args.get("parent_id").and_then(|v| v.as_i64());
178
179 let ctx = ProjectContext::load_or_init()
180 .await
181 .map_err(|e| format!("Failed to load project context: {}", e))?;
182
183 let task_mgr = TaskManager::new(&ctx.pool);
184 let task = task_mgr
185 .add_task(name, spec, parent_id)
186 .await
187 .map_err(|e| format!("Failed to add task: {}", e))?;
188
189 serde_json::to_value(&task).map_err(|e| format!("Serialization error: {}", e))
190}
191
192async fn handle_task_start(args: Value) -> Result<Value, String> {
193 let task_id = args
194 .get("task_id")
195 .and_then(|v| v.as_i64())
196 .ok_or("Missing required parameter: task_id")?;
197
198 let with_events = args
199 .get("with_events")
200 .and_then(|v| v.as_bool())
201 .unwrap_or(true);
202
203 let ctx = ProjectContext::load_or_init()
204 .await
205 .map_err(|e| format!("Failed to load project context: {}", e))?;
206
207 let task_mgr = TaskManager::new(&ctx.pool);
208 let task = task_mgr
209 .start_task(task_id, with_events)
210 .await
211 .map_err(|e| format!("Failed to start task: {}", e))?;
212
213 serde_json::to_value(&task).map_err(|e| format!("Serialization error: {}", e))
214}
215
216async fn handle_task_pick_next(args: Value) -> Result<Value, String> {
217 let _max_count = args.get("max_count").and_then(|v| v.as_i64());
218 let _capacity = args.get("capacity").and_then(|v| v.as_i64());
219
220 let ctx = ProjectContext::load_or_init()
221 .await
222 .map_err(|e| format!("Failed to load project context: {}", e))?;
223
224 let task_mgr = TaskManager::new(&ctx.pool);
225 let response = task_mgr
226 .pick_next()
227 .await
228 .map_err(|e| format!("Failed to pick next task: {}", e))?;
229
230 serde_json::to_value(&response).map_err(|e| format!("Serialization error: {}", e))
231}
232
233async fn handle_task_spawn_subtask(args: Value) -> Result<Value, String> {
234 let name = args
235 .get("name")
236 .and_then(|v| v.as_str())
237 .ok_or("Missing required parameter: name")?;
238
239 let spec = args.get("spec").and_then(|v| v.as_str());
240
241 let ctx = ProjectContext::load_or_init()
242 .await
243 .map_err(|e| format!("Failed to load project context: {}", e))?;
244
245 let task_mgr = TaskManager::new(&ctx.pool);
246 let subtask = task_mgr
247 .spawn_subtask(name, spec)
248 .await
249 .map_err(|e| format!("Failed to spawn subtask: {}", e))?;
250
251 serde_json::to_value(&subtask).map_err(|e| format!("Serialization error: {}", e))
252}
253
254async fn handle_task_switch(args: Value) -> Result<Value, String> {
255 let task_id = args
256 .get("task_id")
257 .and_then(|v| v.as_i64())
258 .ok_or("Missing required parameter: task_id")?;
259
260 let ctx = ProjectContext::load_or_init()
261 .await
262 .map_err(|e| format!("Failed to load project context: {}", e))?;
263
264 let task_mgr = TaskManager::new(&ctx.pool);
265 let task = task_mgr
266 .switch_to_task(task_id)
267 .await
268 .map_err(|e| format!("Failed to switch task: {}", e))?;
269
270 serde_json::to_value(&task).map_err(|e| format!("Serialization error: {}", e))
271}
272
273async fn handle_task_done(args: Value) -> Result<Value, String> {
274 let task_id = args.get("task_id").and_then(|v| v.as_i64());
275
276 let ctx = ProjectContext::load_or_init()
277 .await
278 .map_err(|e| format!("Failed to load project context: {}", e))?;
279
280 let task_mgr = TaskManager::new(&ctx.pool);
281
282 if let Some(id) = task_id {
284 let workspace_mgr = WorkspaceManager::new(&ctx.pool);
285 workspace_mgr
286 .set_current_task(id)
287 .await
288 .map_err(|e| format!("Failed to set current task: {}", e))?;
289 }
290
291 let task = task_mgr
292 .done_task()
293 .await
294 .map_err(|e| format!("Failed to mark task as done: {}", e))?;
295
296 serde_json::to_value(&task).map_err(|e| format!("Serialization error: {}", e))
297}
298
299async fn handle_task_update(args: Value) -> Result<Value, String> {
300 let task_id = args
301 .get("task_id")
302 .and_then(|v| v.as_i64())
303 .ok_or("Missing required parameter: task_id")?;
304
305 let name = args.get("name").and_then(|v| v.as_str());
306 let spec = args.get("spec").and_then(|v| v.as_str());
307 let status = args.get("status").and_then(|v| v.as_str());
308 let complexity = args
309 .get("complexity")
310 .and_then(|v| v.as_i64())
311 .map(|v| v as i32);
312 let priority = args
313 .get("priority")
314 .and_then(|v| v.as_i64())
315 .map(|v| v as i32);
316 let parent_id = args.get("parent_id").and_then(|v| v.as_i64()).map(Some);
317
318 let ctx = ProjectContext::load_or_init()
319 .await
320 .map_err(|e| format!("Failed to load project context: {}", e))?;
321
322 let task_mgr = TaskManager::new(&ctx.pool);
323 let task = task_mgr
324 .update_task(task_id, name, spec, parent_id, status, complexity, priority)
325 .await
326 .map_err(|e| format!("Failed to update task: {}", e))?;
327
328 serde_json::to_value(&task).map_err(|e| format!("Serialization error: {}", e))
329}
330
331async fn handle_task_find(args: Value) -> Result<Value, String> {
332 let status = args.get("status").and_then(|v| v.as_str());
333 let parent = args.get("parent").and_then(|v| v.as_str());
334
335 let parent_opt = parent.map(|p| {
336 if p == "null" {
337 None
338 } else {
339 p.parse::<i64>().ok()
340 }
341 });
342
343 let ctx = ProjectContext::load()
344 .await
345 .map_err(|e| format!("Failed to load project context: {}", e))?;
346
347 let task_mgr = TaskManager::new(&ctx.pool);
348 let tasks = task_mgr
349 .find_tasks(status, parent_opt)
350 .await
351 .map_err(|e| format!("Failed to find tasks: {}", e))?;
352
353 serde_json::to_value(&tasks).map_err(|e| format!("Serialization error: {}", e))
354}
355
356async fn handle_task_get(args: Value) -> Result<Value, String> {
357 let task_id = args
358 .get("task_id")
359 .and_then(|v| v.as_i64())
360 .ok_or("Missing required parameter: task_id")?;
361
362 let with_events = args
363 .get("with_events")
364 .and_then(|v| v.as_bool())
365 .unwrap_or(false);
366
367 let ctx = ProjectContext::load()
368 .await
369 .map_err(|e| format!("Failed to load project context: {}", e))?;
370
371 let task_mgr = TaskManager::new(&ctx.pool);
372
373 if with_events {
374 let task = task_mgr
375 .get_task_with_events(task_id)
376 .await
377 .map_err(|e| format!("Failed to get task: {}", e))?;
378 serde_json::to_value(&task).map_err(|e| format!("Serialization error: {}", e))
379 } else {
380 let task = task_mgr
381 .get_task(task_id)
382 .await
383 .map_err(|e| format!("Failed to get task: {}", e))?;
384 serde_json::to_value(&task).map_err(|e| format!("Serialization error: {}", e))
385 }
386}
387
388async fn handle_event_add(args: Value) -> Result<Value, String> {
389 let task_id = args.get("task_id").and_then(|v| v.as_i64());
390
391 let event_type = args
392 .get("event_type")
393 .and_then(|v| v.as_str())
394 .ok_or("Missing required parameter: event_type")?;
395
396 let data = args
397 .get("data")
398 .and_then(|v| v.as_str())
399 .ok_or("Missing required parameter: data")?;
400
401 let ctx = ProjectContext::load_or_init()
402 .await
403 .map_err(|e| format!("Failed to load project context: {}", e))?;
404
405 let target_task_id = if let Some(id) = task_id {
407 id
408 } else {
409 let current_task_id: Option<String> =
411 sqlx::query_scalar("SELECT value FROM workspace_state WHERE key = 'current_task_id'")
412 .fetch_optional(&ctx.pool)
413 .await
414 .map_err(|e| format!("Database error: {}", e))?;
415
416 current_task_id
417 .and_then(|s| s.parse::<i64>().ok())
418 .ok_or_else(|| {
419 "No current task is set and task_id was not provided. \
420 Use task_start or task_switch to set a task first."
421 .to_string()
422 })?
423 };
424
425 let event_mgr = EventManager::new(&ctx.pool);
426 let event = event_mgr
427 .add_event(target_task_id, event_type, data)
428 .await
429 .map_err(|e| format!("Failed to add event: {}", e))?;
430
431 serde_json::to_value(&event).map_err(|e| format!("Serialization error: {}", e))
432}
433
434async fn handle_event_list(args: Value) -> Result<Value, String> {
435 let task_id = args
436 .get("task_id")
437 .and_then(|v| v.as_i64())
438 .ok_or("Missing required parameter: task_id")?;
439
440 let limit = args.get("limit").and_then(|v| v.as_i64());
441
442 let ctx = ProjectContext::load()
443 .await
444 .map_err(|e| format!("Failed to load project context: {}", e))?;
445
446 let event_mgr = EventManager::new(&ctx.pool);
447 let events = event_mgr
448 .list_events(task_id, limit)
449 .await
450 .map_err(|e| format!("Failed to list events: {}", e))?;
451
452 serde_json::to_value(&events).map_err(|e| format!("Serialization error: {}", e))
453}
454
455async fn handle_current_task_get(_args: Value) -> Result<Value, String> {
456 let ctx = ProjectContext::load()
457 .await
458 .map_err(|e| format!("Failed to load project context: {}", e))?;
459
460 let workspace_mgr = WorkspaceManager::new(&ctx.pool);
461 let response = workspace_mgr
462 .get_current_task()
463 .await
464 .map_err(|e| format!("Failed to get current task: {}", e))?;
465
466 serde_json::to_value(&response).map_err(|e| format!("Serialization error: {}", e))
467}
468
469async fn handle_report_generate(args: Value) -> Result<Value, String> {
470 let since = args.get("since").and_then(|v| v.as_str()).map(String::from);
471 let status = args
472 .get("status")
473 .and_then(|v| v.as_str())
474 .map(String::from);
475 let filter_name = args
476 .get("filter_name")
477 .and_then(|v| v.as_str())
478 .map(String::from);
479 let filter_spec = args
480 .get("filter_spec")
481 .and_then(|v| v.as_str())
482 .map(String::from);
483 let summary_only = args
484 .get("summary_only")
485 .and_then(|v| v.as_bool())
486 .unwrap_or(true);
487
488 let ctx = ProjectContext::load()
489 .await
490 .map_err(|e| format!("Failed to load project context: {}", e))?;
491
492 let report_mgr = ReportManager::new(&ctx.pool);
493 let report = report_mgr
494 .generate_report(since, status, filter_name, filter_spec, summary_only)
495 .await
496 .map_err(|e| format!("Failed to generate report: {}", e))?;
497
498 serde_json::to_value(&report).map_err(|e| format!("Serialization error: {}", e))
499}