enhancement: automatically update MCP tool list UI on server change (#4940)

* enhancement: automatically update MCP tool list UI on server change

* enhancement: tool call block fit content
This commit is contained in:
Louis 2025-04-24 16:47:13 +07:00
parent 946e8dda65
commit 5c88cedaf0
No known key found for this signature in database
GPG Key ID: 44FA9F4D33C37DE2
4 changed files with 19 additions and 11 deletions

View File

@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc};
use rmcp::{service::RunningService, transport::TokioChildProcess, RoleClient, ServiceExt}; use rmcp::{service::RunningService, transport::TokioChildProcess, RoleClient, ServiceExt};
use serde_json::Value; use serde_json::Value;
use tauri::{AppHandle, State}; use tauri::{AppHandle, Emitter, State};
use tokio::{process::Command, sync::Mutex}; use tokio::{process::Command, sync::Mutex};
use super::{cmd::get_jan_data_folder_path, state::AppState}; use super::{cmd::get_jan_data_folder_path, state::AppState};
@ -24,7 +24,6 @@ pub async fn run_mcp_commands(
"Load MCP configs from {}", "Load MCP configs from {}",
app_path.clone() + "/mcp_config.json" app_path.clone() + "/mcp_config.json"
); );
// let mut client_list = HashMap::new();
let config_content = std::fs::read_to_string(app_path.clone() + "/mcp_config.json") let config_content = std::fs::read_to_string(app_path.clone() + "/mcp_config.json")
.map_err(|e| format!("Failed to read config file: {}", e))?; .map_err(|e| format!("Failed to read config file: {}", e))?;
@ -81,10 +80,7 @@ fn extract_command_args(
} }
#[tauri::command] #[tauri::command]
pub async fn restart_mcp_servers( pub async fn restart_mcp_servers(app: AppHandle, state: State<'_, AppState>) -> Result<(), String> {
app: AppHandle,
state: State<'_, AppState>,
) -> Result<(), String> {
let app_path = get_jan_data_folder_path(app.clone()); let app_path = get_jan_data_folder_path(app.clone());
let app_path_str = app_path.to_str().unwrap().to_string(); let app_path_str = app_path.to_str().unwrap().to_string();
let servers = state.mcp_servers.clone(); let servers = state.mcp_servers.clone();
@ -92,7 +88,10 @@ pub async fn restart_mcp_servers(
stop_mcp_servers(state.mcp_servers.clone()).await?; stop_mcp_servers(state.mcp_servers.clone()).await?;
// Restart the servers // Restart the servers
run_mcp_commands(app_path_str, servers).await run_mcp_commands(app_path_str, servers).await?;
app.emit("mcp-update", "MCP servers updated")
.map_err(|e| format!("Failed to emit event: {}", e))
} }
pub async fn stop_mcp_servers( pub async fn stop_mcp_servers(

View File

@ -6,7 +6,7 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use tar::Archive; use tar::Archive;
use tauri::{App, Listener, Manager}; use tauri::{App, Emitter, Listener, Manager};
use tauri_plugin_shell::process::CommandEvent; use tauri_plugin_shell::process::CommandEvent;
use tauri_plugin_shell::ShellExt; use tauri_plugin_shell::ShellExt;
use tauri_plugin_store::StoreExt; use tauri_plugin_store::StoreExt;
@ -185,10 +185,12 @@ pub fn setup_mcp(app: &App) {
let state = app.state::<AppState>().inner(); let state = app.state::<AppState>().inner();
let app_path_str = app_path.to_str().unwrap().to_string(); let app_path_str = app_path.to_str().unwrap().to_string();
let servers = state.mcp_servers.clone(); let servers = state.mcp_servers.clone();
let app_handle = app.handle().clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
if let Err(e) = run_mcp_commands(app_path_str, servers).await { if let Err(e) = run_mcp_commands(app_path_str, servers).await {
log::error!("Failed to run mcp commands: {}", e); log::error!("Failed to run mcp commands: {}", e);
} }
app_handle.emit("mcp-update", "MCP servers updated").unwrap();
}); });
} }

View File

@ -13,6 +13,7 @@ import {
Modal, Modal,
Switch, Switch,
} from '@janhq/joi' } from '@janhq/joi'
import { listen } from '@tauri-apps/api/event'
import { useAtom, useAtomValue } from 'jotai' import { useAtom, useAtomValue } from 'jotai'
import { import {
FileTextIcon, FileTextIcon,
@ -117,6 +118,12 @@ const ChatInput = () => {
window.core?.api?.getTools().then((data: ModelTool[]) => { window.core?.api?.getTools().then((data: ModelTool[]) => {
setTools(data) setTools(data)
}) })
listen('mcp-update', (event) => {
window.core?.api?.getTools().then((data: ModelTool[]) => {
setTools(data)
})
})
}, []) }, [])
const onStopInferenceClick = async () => { const onStopInferenceClick = async () => {

View File

@ -49,11 +49,11 @@ const ToolCallBlock = ({ id, name, result, loading, onExpand }: Props) => {
<ScrollArea <ScrollArea
className={twMerge( className={twMerge(
'w-full overflow-hidden transition-all duration-300', 'h-fit w-full overflow-auto transition-all duration-300',
isExpanded ? 'max-h-96' : 'max-h-0' isExpanded ? 'max-h-44' : 'max-h-0 overflow-hidden'
)} )}
> >
<div className="mt-2 overflow-x-hidden pl-6 text-[hsla(var(--text-secondary))]"> <div className="mt-2 inline-block overflow-x-hidden pl-6 text-[hsla(var(--text-secondary))]">
<span>{result ?? ''} </span> <span>{result ?? ''} </span>
</div> </div>
</ScrollArea> </ScrollArea>