feat: add embedded MCP server and Makefile

Add an MCP (Model Context Protocol) server that starts on 127.0.0.1:9427
at app launch, sharing connection pools with the Tauri IPC layer. This
lets Claude (or any MCP client) query PostgreSQL through Tusk's existing
connections.

MCP tools: list_connections, execute_query, list_schemas, list_tables,
describe_table.

Also add a Makefile with targets for dev, build (cross-platform),
install/uninstall, lint, and formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 13:24:25 +03:00
parent ded35d8c40
commit 32486b0524
10 changed files with 756 additions and 71 deletions

View File

@@ -3,11 +3,12 @@ use crate::models::management::*;
use crate::state::AppState;
use crate::utils::escape_ident;
use sqlx::Row;
use std::sync::Arc;
use tauri::State;
#[tauri::command]
pub async fn get_database_info(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
) -> TuskResult<Vec<DatabaseInfo>> {
let pools = state.pools.read().await;
@@ -54,7 +55,7 @@ pub async fn get_database_info(
#[tauri::command]
pub async fn create_database(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
params: CreateDatabaseParams,
) -> TuskResult<()> {
@@ -95,7 +96,7 @@ pub async fn create_database(
#[tauri::command]
pub async fn drop_database(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
name: String,
) -> TuskResult<()> {
@@ -129,7 +130,7 @@ pub async fn drop_database(
#[tauri::command]
pub async fn list_roles(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
) -> TuskResult<Vec<RoleInfo>> {
let pools = state.pools.read().await;
@@ -193,7 +194,7 @@ pub async fn list_roles(
#[tauri::command]
pub async fn create_role(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
params: CreateRoleParams,
) -> TuskResult<()> {
@@ -271,7 +272,7 @@ pub async fn create_role(
#[tauri::command]
pub async fn alter_role(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
params: AlterRoleParams,
) -> TuskResult<()> {
@@ -370,7 +371,7 @@ pub async fn alter_role(
#[tauri::command]
pub async fn drop_role(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
name: String,
) -> TuskResult<()> {
@@ -394,7 +395,7 @@ pub async fn drop_role(
#[tauri::command]
pub async fn get_table_privileges(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
schema: String,
table: String,
@@ -433,7 +434,7 @@ pub async fn get_table_privileges(
#[tauri::command]
pub async fn grant_revoke(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
params: GrantRevokeParams,
) -> TuskResult<()> {
@@ -478,7 +479,7 @@ pub async fn grant_revoke(
#[tauri::command]
pub async fn manage_role_membership(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
params: RoleMembershipParams,
) -> TuskResult<()> {
@@ -510,7 +511,7 @@ pub async fn manage_role_membership(
#[tauri::command]
pub async fn list_sessions(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
) -> TuskResult<Vec<SessionInfo>> {
let pools = state.pools.read().await;
@@ -550,7 +551,7 @@ pub async fn list_sessions(
#[tauri::command]
pub async fn cancel_query(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
pid: i32,
) -> TuskResult<bool> {
@@ -570,7 +571,7 @@ pub async fn cancel_query(
#[tauri::command]
pub async fn terminate_backend(
state: State<'_, AppState>,
state: State<'_, Arc<AppState>>,
connection_id: String,
pid: i32,
) -> TuskResult<bool> {