Skip to content

Commit 3b57c8b

Browse files
committed
perf: add mcp server
1 parent aa0e797 commit 3b57c8b

11 files changed

Lines changed: 128 additions & 116 deletions

File tree

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ FORWARDED_ALLOW_IPS='*'
2020
SCARF_NO_ANALYTICS=true
2121
DO_NOT_TRACK=true
2222
ANONYMIZED_TELEMETRY=false
23+
TOOL_SERVER_CONNECTIONS=''
24+
OPEN_WEBUI_HOST=""

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,4 +311,4 @@ cypress/videos
311311
cypress/screenshots
312312
.vscode/settings.json
313313

314-
/backend/open_webui/jms/data/
314+
/backend/open_webui/jms/data/

.vscode/launch.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"cwd": "${workspaceFolder}/backend",
3030
"console": "integratedTerminal",
3131
"env": {
32-
"PORT": "8083"
32+
"PORT": "8083",
33+
"GLOBAL_LOG_LEVEL": "DEBUG"
3334
}
3435
}
3536
]

backend/mcp_servers.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"mcpServers": {
3+
"memory": {
4+
"command": "npx",
5+
"args": ["-y", "@modelcontextprotocol/server-memory"]
6+
},
7+
"time": {
8+
"command": "uvx",
9+
"args": ["mcp-server-time", "--local-timezone=America/New_York"],
10+
"disabledTools": ["convert_time"]
11+
},
12+
"jumpserver-mcp": {
13+
"type": "streamable-http",
14+
"url": "http://127.0.0.1:8000/mcp"
15+
}
16+
}
17+
}

backend/mcpo.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
3+
mcpo --port 8003 --config ./mcp_servers.json --hot-reload

backend/open_webui/config.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,10 +1089,33 @@ def feishu_oauth_register(oauth: OAuth):
10891089
log.exception(f"Error loading TOOL_SERVER_CONNECTIONS: {e}")
10901090
tool_server_connections = []
10911091

1092+
INTERNAL_TOOL_SERVER_CONNECTIONS = [
1093+
{
1094+
"url": "http://localhost:8000/mcp",
1095+
"path": "openapi.json",
1096+
"type": "mcp",
1097+
"auth_type": "session",
1098+
"key": "",
1099+
"config": {
1100+
"enable": True,
1101+
"access_control": None
1102+
},
1103+
"spec_type": "url",
1104+
"spec": "",
1105+
"info": {
1106+
"id": "server-l0vE",
1107+
"name": "jms-mcp",
1108+
"description": "JumpServer MCP Server"
1109+
}
1110+
}
1111+
]
1112+
1113+
1114+
10921115
TOOL_SERVER_CONNECTIONS = PersistentConfig(
10931116
"TOOL_SERVER_CONNECTIONS",
1094-
"tool_server.connections",
1095-
tool_server_connections,
1117+
"tool_server.connectionss",
1118+
INTERNAL_TOOL_SERVER_CONNECTIONS,
10961119
)
10971120

10981121
####################################

backend/open_webui/env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@
111111

112112
log.setLevel(SRC_LOG_LEVELS["CONFIG"])
113113

114-
WEBUI_NAME = os.environ.get("WEBUI_NAME", "JumpServer Chat AI &")
114+
WEBUI_NAME = os.environ.get("WEBUI_NAME", "JumpServer Chat AI")
115115
if WEBUI_NAME != "Open WebUI":
116116
WEBUI_NAME += " (Open WebUI)"
117117

backend/open_webui/utils/auth.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,11 @@ def get_current_user(
259259
detail=ERROR_MESSAGES.UNAUTHORIZED,
260260
)
261261

262+
if user.username in ["admin", "superadmin"]:
263+
user.role = "admin"
264+
else:
265+
user.role = "user"
266+
262267
# Add user info to current span
263268
current_span = trace.get_current_span()
264269
if current_span:

src/lib/components/chat/MessageInput/IntegrationsMenu.svelte

Lines changed: 66 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
2020
import Photo from '$lib/components/icons/Photo.svelte';
2121
import Terminal from '$lib/components/icons/Terminal.svelte';
22-
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
23-
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
2422
2523
const i18n = getContext('i18n');
2624
@@ -62,6 +60,7 @@
6260
if ($_tools === null) {
6361
await _tools.set(await getTools(localStorage.token));
6462
}
63+
console.log('------ Tools: ------', localStorage.token, $_tools);
6564
6665
if ($_tools) {
6766
tools = $_tools.reduce((a, tool, i, arr) => {
@@ -116,25 +115,75 @@
116115
<div in:fly={{ x: -20, duration: 150 }}>
117116
{#if tools}
118117
{#if Object.keys(tools).length > 0}
119-
<button
120-
class="flex w-full justify-between gap-2 items-center px-3 py-1.5 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800/50"
121-
on:click={() => {
122-
tab = 'tools';
123-
}}
124-
>
125-
<Wrench />
118+
{#each Object.keys(tools) as toolId}
119+
<button
120+
class="relative flex w-full justify-between gap-2 items-center px-3 py-1.5 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800/50"
121+
on:click={async (e) => {
122+
if (!(tools[toolId]?.authenticated ?? true)) {
123+
e.preventDefault();
124+
125+
let parts = toolId.split(':');
126+
let serverId = parts?.at(-1) ?? toolId;
126127

127-
<div class="flex items-center w-full justify-between">
128-
<div class=" line-clamp-1">
129-
{$i18n.t('Tools')}
130-
<span class="ml-0.5 text-gray-500">{Object.keys(tools).length}</span>
128+
const authUrl = getOAuthClientAuthorizationUrl(serverId, 'mcp');
129+
window.open(authUrl, '_self', 'noopener');
130+
} else {
131+
tools[toolId].enabled = !tools[toolId].enabled;
132+
133+
const state = tools[toolId].enabled;
134+
await tick();
135+
136+
if (state) {
137+
selectedToolIds = [...selectedToolIds, toolId];
138+
} else {
139+
selectedToolIds = selectedToolIds.filter((id) => id !== toolId);
140+
}
141+
}
142+
}}
143+
>
144+
{#if !(tools[toolId]?.authenticated ?? true)}
145+
<!-- make it slighly darker and not clickable -->
146+
<div class="absolute inset-0 opacity-50 rounded-xl cursor-pointer z-10" />
147+
{/if}
148+
<div class="flex-1 truncate">
149+
<div class="flex flex-1 gap-2 items-center">
150+
<Tooltip content={tools[toolId]?.name ?? ''} placement="top">
151+
<div class="shrink-0">
152+
<Wrench />
153+
</div>
154+
</Tooltip>
155+
<Tooltip content={tools[toolId]?.description ?? ''} placement="top-start">
156+
<div class=" truncate">{tools[toolId].name}</div>
157+
</Tooltip>
158+
</div>
131159
</div>
132160

133-
<div class="text-gray-500">
134-
<ChevronRight />
161+
{#if tools[toolId]?.has_user_valves}
162+
<div class=" shrink-0">
163+
<Tooltip content={$i18n.t('Valves')}>
164+
<button
165+
class="self-center w-fit text-sm text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition rounded-full"
166+
type="button"
167+
on:click={(e) => {
168+
e.stopPropagation();
169+
e.preventDefault();
170+
onShowValves({
171+
type: 'tool',
172+
id: toolId
173+
});
174+
}}
175+
>
176+
<Knobs />
177+
</button>
178+
</Tooltip>
179+
</div>
180+
{/if}
181+
182+
<div class=" shrink-0">
183+
<Switch state={tools[toolId].enabled} />
135184
</div>
136-
</div>
137-
</button>
185+
</button>
186+
{/each}
138187
{/if}
139188
{:else}
140189
<div class="py-4">
@@ -310,94 +359,6 @@
310359
</Tooltip>
311360
{/if}
312361
</div>
313-
{:else if tab === 'tools' && tools}
314-
<div in:fly={{ x: 20, duration: 150 }}>
315-
<button
316-
class="flex w-full justify-between gap-2 items-center px-3 py-1.5 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800/50"
317-
on:click={() => {
318-
tab = '';
319-
}}
320-
>
321-
<ChevronLeft />
322-
323-
<div class="flex items-center w-full justify-between">
324-
<div>
325-
{$i18n.t('Tools')}
326-
<span class="ml-0.5 text-gray-500">{Object.keys(tools).length}</span>
327-
</div>
328-
</div>
329-
</button>
330-
331-
{#each Object.keys(tools) as toolId}
332-
<button
333-
class="relative flex w-full justify-between gap-2 items-center px-3 py-1.5 text-sm cursor-pointer rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800/50"
334-
on:click={async (e) => {
335-
if (!(tools[toolId]?.authenticated ?? true)) {
336-
e.preventDefault();
337-
338-
let parts = toolId.split(':');
339-
let serverId = parts?.at(-1) ?? toolId;
340-
341-
const authUrl = getOAuthClientAuthorizationUrl(serverId, 'mcp');
342-
window.open(authUrl, '_self', 'noopener');
343-
} else {
344-
tools[toolId].enabled = !tools[toolId].enabled;
345-
346-
const state = tools[toolId].enabled;
347-
await tick();
348-
349-
if (state) {
350-
selectedToolIds = [...selectedToolIds, toolId];
351-
} else {
352-
selectedToolIds = selectedToolIds.filter((id) => id !== toolId);
353-
}
354-
}
355-
}}
356-
>
357-
{#if !(tools[toolId]?.authenticated ?? true)}
358-
<!-- make it slighly darker and not clickable -->
359-
<div class="absolute inset-0 opacity-50 rounded-xl cursor-pointer z-10" />
360-
{/if}
361-
<div class="flex-1 truncate">
362-
<div class="flex flex-1 gap-2 items-center">
363-
<Tooltip content={tools[toolId]?.name ?? ''} placement="top">
364-
<div class="shrink-0">
365-
<Wrench />
366-
</div>
367-
</Tooltip>
368-
<Tooltip content={tools[toolId]?.description ?? ''} placement="top-start">
369-
<div class=" truncate">{tools[toolId].name}</div>
370-
</Tooltip>
371-
</div>
372-
</div>
373-
374-
{#if tools[toolId]?.has_user_valves}
375-
<div class=" shrink-0">
376-
<Tooltip content={$i18n.t('Valves')}>
377-
<button
378-
class="self-center w-fit text-sm text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition rounded-full"
379-
type="button"
380-
on:click={(e) => {
381-
e.stopPropagation();
382-
e.preventDefault();
383-
onShowValves({
384-
type: 'tool',
385-
id: toolId
386-
});
387-
}}
388-
>
389-
<Knobs />
390-
</button>
391-
</Tooltip>
392-
</div>
393-
{/if}
394-
395-
<div class=" shrink-0">
396-
<Switch state={tools[toolId].enabled} />
397-
</div>
398-
</button>
399-
{/each}
400-
</div>
401362
{/if}
402363
</DropdownMenu.Content>
403364
</div>

src/lib/components/chat/SettingsModal.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@
841841
{/if}
842842
{#if $user?.role === 'admin'}
843843
<a
844-
href="/admin/settings"
844+
href="/kael/admin/settings"
845845
class="px-0.5 md:px-2.5 py-1 min-w-fit rounded-xl flex-1 md:flex-none md:mt-auto flex text-left transition {$settings?.highContrastMode
846846
? 'hover:bg-gray-200 dark:hover:bg-gray-800'
847847
: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"

0 commit comments

Comments
 (0)