diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx
index b3400bd..080d363 100644
--- a/src/components/Settings.tsx
+++ b/src/components/Settings.tsx
@@ -84,6 +84,20 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
const [urlRegisterLoading, setUrlRegisterLoading] = useState(false)
const [appQuitLoading, setAppQuitLoading] = useState(false)
const [appRestartLoading, setAppRestartLoading] = useState(false)
+ const [mcpLoading, setMcpLoading] = useState(false)
+ const [mcpConfig, setMcpConfig] = useState<{ host: string; port: number }>({
+ host: "127.0.0.1",
+ port: 3901,
+ })
+ const [mcpStatus, setMcpStatus] = useState<{
+ is_running: boolean
+ config: { host: string; port: number }
+ url?: string | null
+ }>({
+ is_running: false,
+ config: { host: "127.0.0.1", port: 3901 },
+ url: null,
+ })
const canAdmin = permission === "admin"
const [messageApi, contextHolder] = message.useMessage()
@@ -115,6 +129,21 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
window.dispatchEvent(new CustomEvent("ss:data-updated", { detail: { category } }))
}
+ const loadMcpStatus = async () => {
+ if (!(window as any).api?.mcpServerStatus) return
+ try {
+ const res = await (window as any).api.mcpServerStatus()
+ if (res.success && res.data) {
+ setMcpStatus(res.data)
+ if (res.data.config?.host && res.data.config?.port) {
+ setMcpConfig({ host: res.data.config.host, port: res.data.config.port })
+ }
+ }
+ } catch {
+ // ignore status polling errors in settings page
+ }
+ }
+
const loadAll = async () => {
if (!(window as any).api) return
const res = await (window as any).api.getAllSettings()
@@ -125,6 +154,7 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
}
const authRes = await (window as any).api.authGetStatus()
if (authRes.success && authRes.data) setSecurityStatus(authRes.data)
+ await loadMcpStatus()
}
useEffect(() => {
@@ -456,6 +486,61 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
}
}
+ const startMcpServer = async () => {
+ if (!(window as any).api?.mcpServerStart) return
+ const host = mcpConfig.host.trim()
+ const port = Number(mcpConfig.port)
+ if (!host) {
+ messageApi.warning(t("settings.mcp.hostRequired"))
+ return
+ }
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) {
+ messageApi.warning(t("settings.mcp.portInvalid"))
+ return
+ }
+
+ setMcpLoading(true)
+ try {
+ const res = await withTimeout(
+ (window as any).api.mcpServerStart({ host, port }),
+ 10_000,
+ t("settings.mcp.startTimeout")
+ )
+ if (res.success) {
+ messageApi.success(t("settings.mcp.startSuccess"))
+ } else {
+ messageApi.error(res.message || t("settings.mcp.startFailed"))
+ }
+ } catch (e: any) {
+ messageApi.error(e?.message || t("settings.mcp.startFailed"))
+ } finally {
+ await loadMcpStatus()
+ setMcpLoading(false)
+ }
+ }
+
+ const stopMcpServer = async () => {
+ if (!(window as any).api?.mcpServerStop) return
+ setMcpLoading(true)
+ try {
+ const res = await withTimeout(
+ (window as any).api.mcpServerStop(),
+ 10_000,
+ t("settings.mcp.stopTimeout")
+ )
+ if (res.success) {
+ messageApi.success(t("settings.mcp.stopSuccess"))
+ } else {
+ messageApi.error(res.message || t("settings.mcp.stopFailed"))
+ }
+ } catch (e: any) {
+ messageApi.error(e?.message || t("settings.mcp.stopFailed"))
+ } finally {
+ await loadMcpStatus()
+ setMcpLoading(false)
+ }
+ }
+
const currentYear = new Date().getFullYear()
const confirmQuitApp = () => {
@@ -1057,6 +1142,76 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission