feat(mcp): Add 5 developer enhancements (git, logs, clipboard watcher, ast skeleton, nvim ghost text)

This commit is contained in:
Riz Ashraf committed 2026-09-30 05:29:50 +01:00
1 parent ccc268d807
commit 0e9748f797
27 files changed
+1401 -174

No files matched your search

+50 -7
View File
@@ -23,6 +23,7 @@ pub fn create_router(app_state: Arc<AppState>) -> Router {
)
.route("/ws", get(ws_handler))
.route("/health", get(health_handler))
.route("/ping", get(|| async move { (axum::http::StatusCode::OK, "pong") }))
.route("/nvim/telemetry", post(nvim_telemetry_handler))
.route("/api/terminal/history", get(crate::api::telemetry::get_terminal_history_handler))
.route("/terminal/telemetry", post(crate::api::telemetry::terminal_telemetry_handler))
@@ -50,13 +51,12 @@ pub fn create_router(app_state: Arc<AppState>) -> Router {
return (axum::http::StatusCode::UNAUTHORIZED, "Unauthorized").into_response();
}
std::thread::spawn(|| {
tracing::info!(
"Received shutdown request via /shutdown endpoint. Exiting process cleanly."
);
std::thread::sleep(std::time::Duration::from_millis(100));
std::process::exit(0);
});
tracing::info!(
"Received shutdown request via /shutdown endpoint. Initiating graceful shutdown."
);
if let Some(tx) = state.shutdown_tx.lock().unwrap().take() {
let _ = tx.send(());
}
(axum::http::StatusCode::OK, "Shutting down...").into_response()
},
),
@@ -279,6 +279,48 @@ pub fn create_router(app_state: Arc<AppState>) -> Router {
}
}),
)
.route(
"/api/clipboard/capture",
post({
move || async move {
let result = tokio::task::spawn_blocking(move || -> Result<String, String> {
let mut clipboard = arboard::Clipboard::new()
.map_err(|e| format!("Failed to access clipboard: {}", e))?;
let image_data = clipboard.get_image()
.map_err(|e| format!("No image found in clipboard (or unsupported format): {}", e))?;
let img: image::RgbaImage = image::ImageBuffer::from_raw(
image_data.width as u32,
image_data.height as u32,
image_data.bytes.into_owned(),
)
.ok_or("Failed to parse raw clipboard image data")?;
let mut dynamic_img = image::DynamicImage::ImageRgba8(img);
let max_dim = 1024;
if dynamic_img.width() > max_dim || dynamic_img.height() > max_dim {
dynamic_img = dynamic_img.resize(max_dim, max_dim, image::imageops::FilterType::Lanczos3);
}
let rgb_img = dynamic_img.into_rgb8();
use base64::{Engine as _, engine::general_purpose::STANDARD};
let mut cursor = std::io::Cursor::new(Vec::new());
rgb_img.write_to(&mut cursor, image::ImageFormat::Jpeg).map_err(|e| e.to_string())?;
let b64 = STANDARD.encode(cursor.into_inner());
Ok(b64)
})
.await
.map_err(|e| format!("Task panic: {}", e))
.and_then(|r| r);
match result {
Ok(b64) => axum::Json(serde_json::json!({ "success": true, "image": format!("data:image/jpeg;base64,{}", b64) })),
Err(e) => axum::Json(serde_json::json!({ "success": false, "error": e }))
}
}
})
)
.with_state(app_state)
}
@@ -318,3 +360,4 @@ mod tests {
assert_eq!(response.status(), 200);
}
}
// Force recompile