OpenAPI tool servers — frontend sends empty tool_servers=[] so middleware never injects schemas #21805
Replies: 5 comments
|
Updated the description after further analysis and arriving at a fix that works in my environment. Will submit a second related issue, both of which must be fixed to get this working. |
|
Admin level connections do NOT require tool_servers in the payload. |
|
Confirming this on a separate setup with a full root-cause trace through the current Environment
Symptom Server saves successfully, spec loads successfully, but no tools ever appear in the chat "+" menu or in Root cause (traced in There are two separate, unconnected config stores for OpenAPI tool servers. Admin panel ( Chat UI's tool store is populated exclusively from the per-user settings object, not the admin config, in const setToolServers = async () => {
let toolServersData = await getToolServersData($settings?.toolServers ?? []);
toolServersData = toolServersData.filter(...);
toolServers.set(toolServersData);
};
The actual chat request only ever includes tool servers the user explicitly toggled on, filtered from that same Backend takes this list as-is with no fallback to the server-side The only admin-config-driven path that does work end-to-end server-side is for Net effect: admin-level OpenAPI-type tool servers are saved and even fetched/cached server-side ( Workaround: re-add the same OpenAPI tool server under the individual user's own Settings → Tools (not the Admin panel). It then appears in the "+" menu and gets embedded in Suggested fix direction: either (a) merge admin-global, publicly-enabled Happy to open this as a standalone bug report with a minimal repro if that's more useful than a comment here. |
|
Follow-up with harder evidence — this also affects personal-settings ("direct") OpenAPI tool servers once more than one is configured, not just admin-global ones. Setup: Two personal OpenAPI tool servers configured under Settings → Tools: "Home Assistant" (MCP type, unrelated) and "mcp-pve" (OpenAPI type, exposes Test method: Patched Finding 1 — With both "Home Assistant" and "mcp-pve" toggled ON: {"hasTools": false, "tool_servers": 0, "model": "qwen3:8b"}Model response: "nicht in der Liste der verfügbaren Tools enthalten" (not in the list of available tools) — despite both showing as enabled in the UI. Finding 2 — with exactly 1 server active, the wrong server's tools are sent: Toggled "Home Assistant" OFF and "mcp-pve" ON (verified via the UI — only mcp-pve's toggle was green), same prompt: {"hasTools": false, "tool_servers": 1, "model": "qwen3:8b"}
So the single entry that did make it into Root cause hypothesis (from for (const toolId of selectedToolIds) {
if (toolId.startsWith('direct_server:')) {
let serverId = toolId.replace('direct_server:', '');
if (!isNaN(parseInt(serverId))) { toolServerIds.push(parseInt(serverId)); }
else { toolServerIds.push(serverId); }
} else { toolIds.push(toolId); }
}
...
tool_servers: [
...($toolServers ?? []).filter((server, idx) =>
toolServerIds.includes(idx) || toolServerIds.includes(server?.id))
]This filters Independently confirmed the model itself is not at fault: called Happy to provide the full captured request/response pairs or a minimal two-tool-server repro config if useful. |
|
Confirming this — hit the exact same issue (admin-configured OpenAPI tool server, Traced it to the same root cause described above: tool_ids = metadata.get('tool_ids', None)
direct_tool_servers = metadata.get('tool_servers', None)
...
if tool_ids: # entire block skipped when both are empty
...Worth noting: the existing Applied a minimal fallback right after those two Tested against a real self-hosted setup (0.11.0) with an external FastAPI-based tool server: before the patch, a chat request's Happy to open this as a proper PR with tests if that's useful — wanted to confirm the diagnosis and check preferred direction first, since a "silently activate all enabled admin tool servers for every chat" default might not be the shape the maintainers want upstream (e.g. some installs may prefer this scoped per-model/per-chat rather than global-always-on). Full patch (applied via a small idempotent script against the running container, not a source-tree PR) here for reference: https://gist.github.com/Z-Rick84/8a07bd829eb44bcbd3ee9311f312fb8e |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Check Existing Issues
Installation Method
Docker
Open WebUI Version
v0.8.3 (Docker,
ghcr.io/open-webui/open-webui:main, built 2026-02-14)Ollama Version (if applicable)
0.16.3
Operating System
Windows 11 + WSL2 Ubuntu 24.04
Browser (if applicable)
No response
Confirmation
README.md.Description
When OpenAPI-type tool servers are configured in Admin Panel > Settings > External Tools, the model never receives their schemas and cannot call any tools. The failure is completely silent — no error is shown to the user and the chat completes normally without tool use.
Expected Behavior
The
tool_serversfield in the request payload should contain the OpenAPI specs for all enabled tool servers, so thatmiddleware.pycan inject them as atoolsarray into the upstream model request.Actual Behavior
The
tool_serversfield is always[](empty array) even when tool servers are configured, verified, and toggled on.The
tool_idsfield is correctly populated (e.g.["server:1", "server:open-meteo", "server:3"]) but the middleware receives no specs to work with.Steps to Reproduce
OpenAPI/api/chat/completionsrequest in browser DevTools > Network tabLogs & Screenshots
Request payload from DevTools:
{ "model": "my-model", "messages": [{"role": "user", "content": "What is the weather in New York?"}], "tool_ids": ["server:1", "server:open-meteo", "server:3"], "tool_servers": [], ... }Logs
Backend log during the request:
No tool-related log lines appear. By contrast, MCP-type tool servers (with
server:mcp:*prefixed IDs) work correctly because their execution path is fully server-side.Additional Information
#21770 is directly related -- the other half of the same fix.
Root Cause Analysis (traced through source)
middleware.pyline 2274 reads tool server specs from the frontend request:Line 2441 skips the entire tool injection block if this is falsy:
An empty list
[]is falsy, so no tools are ever injected.The backend does have a populated cache:
app.state.TOOL_SERVERSis correctly built at startup bymain.py:However,
app.state.TOOL_SERVERS(andget_tool_servers()) is never referenced inmiddleware.py:$ grep -n "TOOL_SERVERS\|get_tool_servers" /app/backend/open_webui/utils/middleware.py (no output)The middleware relies entirely on the frontend to embed specs in the request, but the frontend never does so for admin-configured OpenAPI tool servers.
Suggested Fix
After line 2274 in
middleware.py, add a fallback to the server-side cache when the frontend sends an empty array:Note:
get_tool_serversis already importable fromopen_webui.utils.tools— it just needs to be added to the import at the top ofmiddleware.py.The
tool_idsmatching needs to check bothserver:{id}(string name) andserver:{idx+1}(1-based numeric position) because the frontend generates IDs inconsistently — some servers get their configured string ID, others get a positional integer.Additional Notes
server:mcp:*IDs) are unaffected — they are handled server-side and work correctlyUSER_PERMISSIONS_FEATURES_DIRECT_TOOL_SERVERSconfig suggests frontend-embedded specs are intended as a user-facing "direct tool servers" feature — admin-configured OpenAPI servers appear to share this broken path unintentionallyAll reactions