diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py
index 4c8289578e..5cd7377876 100644
--- a/backend/open_webui/utils/tools.py
+++ b/backend/open_webui/utils/tools.py
@@ -588,28 +588,20 @@ async def get_tool_server_data(token: str, url: str) -> Dict[str, Any]:
error = str(err)
raise Exception(error)
- data = {
- "openapi": res,
- "info": res.get("info", {}),
- "specs": convert_openapi_to_tool_payload(res),
- }
-
- log.info(f"Fetched data: {data}")
- return data
+ log.debug(f"Fetched data: {res}")
+ return res
async def get_tool_servers_data(servers: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
# Prepare list of enabled servers along with their original index
+
+ tasks = []
server_entries = []
for idx, server in enumerate(servers):
if (
server.get("config", {}).get("enable")
and server.get("type", "openapi") == "openapi"
):
- # Path (to OpenAPI spec URL) can be either a full URL or a path to append to the base URL
- openapi_path = server.get("path", "openapi.json")
- full_url = get_tool_server_url(server.get("url"), openapi_path)
-
info = server.get("info", {})
auth_type = server.get("auth_type", "bearer")
@@ -625,12 +617,34 @@ async def get_tool_servers_data(servers: List[Dict[str, Any]]) -> List[Dict[str,
if not id:
id = str(idx)
- server_entries.append((id, idx, server, full_url, info, token))
+ server_url = server.get("url")
+ spec_type = server.get("spec_type", "url")
- # Create async tasks to fetch data
- tasks = [
- get_tool_server_data(token, url) for (_, _, _, url, _, token) in server_entries
- ]
+ # Create async tasks to fetch data
+ task = None
+ if spec_type == "url":
+ # Path (to OpenAPI spec URL) can be either a full URL or a path to append to the base URL
+ openapi_path = server.get("path", "openapi.json")
+ spec_url = get_tool_server_url(server_url, openapi_path)
+ # Fetch from URL
+ task = get_tool_server_data(token, spec_url)
+ elif spec_type == "json" and server.get("spec", ""):
+ # Use provided JSON spec
+ spec_json = None
+ try:
+ spec_json = json.loads(server.get("spec", ""))
+ except Exception as e:
+ log.error(f"Error parsing JSON spec for tool server {id}: {e}")
+
+ if spec_json:
+ task = asyncio.sleep(
+ 0,
+ result=spec_json,
+ )
+
+ if task:
+ tasks.append(task)
+ server_entries.append((id, idx, server, server_url, info, token))
# Execute tasks concurrently
responses = await asyncio.gather(*tasks, return_exceptions=True)
@@ -642,8 +656,13 @@ async def get_tool_servers_data(servers: List[Dict[str, Any]]) -> List[Dict[str,
log.error(f"Failed to connect to {url} OpenAPI tool server")
continue
- openapi_data = response.get("openapi", {})
+ response = {
+ "openapi": response,
+ "info": response.get("info", {}),
+ "specs": convert_openapi_to_tool_payload(response),
+ }
+ openapi_data = response.get("openapi", {})
if info and isinstance(openapi_data, dict):
openapi_data["info"] = openapi_data.get("info", {})
diff --git a/src/lib/components/AddToolServerModal.svelte b/src/lib/components/AddToolServerModal.svelte
index c6894ddeeb..d47de8ebbd 100644
--- a/src/lib/components/AddToolServerModal.svelte
+++ b/src/lib/components/AddToolServerModal.svelte
@@ -27,11 +27,14 @@
export let direct = false;
export let connection = null;
- let url = '';
- let path = 'openapi.json';
-
let type = 'openapi'; // 'openapi', 'mcp'
+ let url = '';
+
+ let spec_type = 'url'; // 'url', 'json'
+ let spec = ''; // used when spec_type is 'json'
+ let path = 'openapi.json';
+
let auth_type = 'bearer';
let key = '';
@@ -149,10 +152,26 @@
return;
}
+ // validate spec
+ if (spec_type === 'json') {
+ try {
+ const specJSON = JSON.parse(spec);
+ spec = JSON.stringify(specJSON, null, 2);
+ } catch (e) {
+ toast.error($i18n.t('Please enter a valid JSON spec'));
+ loading = false;
+ return;
+ }
+ }
+
const connection = {
- url,
- path,
type,
+ url,
+
+ spec_type,
+ spec,
+ path,
+
auth_type,
key,
config: {
@@ -173,9 +192,12 @@
show = false;
// reset form
- url = '';
- path = 'openapi.json';
type = 'openapi';
+ url = '';
+
+ spec_type = 'url';
+ spec = '';
+ path = 'openapi.json';
key = '';
auth_type = 'bearer';
@@ -191,10 +213,13 @@
const init = () => {
if (connection) {
+ type = connection?.type ?? 'openapi';
url = connection.url;
+
+ spec_type = connection?.spec_type ?? 'url';
+ spec = connection?.spec ?? '';
path = connection?.path ?? 'openapi.json';
- type = connection?.type ?? 'openapi';
auth_type = connection?.auth_type ?? 'bearer';
key = connection?.key ?? '';
@@ -326,35 +351,81 @@