Skip to content

Additional MCP Server functionality#72

Open
rahxam wants to merge 5 commits intoYambr:mainfrom
rahxam:feat/per-server-mcp-auth
Open

Additional MCP Server functionality#72
rahxam wants to merge 5 commits intoYambr:mainfrom
rahxam:feat/per-server-mcp-auth

Conversation

@rahxam
Copy link
Copy Markdown

@rahxam rahxam commented Apr 24, 2026

We have the requirement to use not only MCP servers through litellm, but also MCP servers from other source. We also need the ability to authenticate against the MCP servers with the oauth token of the user. Therefore, I suggest that forwarding a full mcp config should be allowed, as well as the forwarding of the oauth token. If you do not like the solution I could also image to extend the settings wrapper to handle the mcp config.

(Also the describe image skill does not forward the user header to litellm)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added OAuth token support for MCP server authentication.
    • Introduced JSON-based MCP server configuration option alongside existing CSV format.
    • Added new MCP_SERVERS setting for flexible server selection and configuration.
    • Enabled per-user identification tracking in Vision API requests.
  • Improvements

    • Enhanced authentication header handling for MCP servers with flexible token injection methods.

maximilian-hartig-orbis and others added 5 commits April 24, 2026 10:55
UserValves in the OpenWebUI tool now accept two new fields:
- MCP_SERVERS: comma-separated server names (existing) OR a full MCP JSON
  config object for custom server definitions outside the LiteLLM proxy pattern.
  JSON servers support an "authToken" shorthand field that is expanded to
  headers.Authorization and stripped before writing ~/.mcp.json.
- OAUTH_TOKEN: bearer token for MCP server auth. Accepts a plain string
  (applied to all servers) or a JSON map {"server": "token"} for per-server
  tokens. Servers with an explicit Authorization header are never overwritten.

Backend changes:
- context_vars: add current_mcp_oauth_token ContextVar
- docker_manager.build_mcp_config: auto-detect JSON vs CSV input; resolve
  per-server auth via authToken shorthand > headers.Authorization > token
  map > global token > runtime ANTHROPIC_AUTH_TOKEN (container env)
- docker_manager.build_mcp_config_write_script: skip ANTHROPIC_AUTH_TOKEN
  injection for servers that already carry an Authorization header
- mcp_tools.set_context_from_headers: handle X-Mcp-Config (base64 JSON,
  takes priority over X-Mcp-Servers CSV) and X-Mcp-OAuth-Token headers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace static OAUTH_TOKEN valve with OpenWebUI's built-in OAuth session
injection mechanism. All five tool methods now accept __oauth_token__: dict
and thread it through _run_tool → _build_mcp_headers.

Auth token priority (per MCP server):
  1. authToken field in JSON MCP config (per-server, embedded)
  2. OAUTH_TOKEN UserValve (explicit override, plain string or JSON map)
  3. __oauth_token__["access_token"] from active OAuth session (automatic)
  4. Runtime ANTHROPIC_AUTH_TOKEN env var in container (fallback)

Also fixes: MCP_SERVERS and OAUTH_TOKEN were defined in Valves (admin-level)
but read from user_valves — moved to UserValves so users can configure them
without admin access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rValves

- Move MCP_SERVERS back into Valves (admin-only, not per-user)
- Replace static OAUTH_TOKEN field with FORWARD_OAUTH_TOKEN bool flag:
  when enabled, the user's OAuth session access_token is forwarded as
  X-Mcp-OAuth-Token header; servers with explicit authToken are unaffected
- Remove UserValves class entirely
- _build_mcp_headers now reads purely from self.valves — no user_valves lookup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… container

MCP_SERVERS JSON config now supports explicit per-server auth control:
  "authToken": "\$oauth"          → embeds current OAuth access_token at call time
  "authToken": "\$SOME_ENV_VAR"   → resolved from container env at write time
  "authToken": "literal"          → embedded as-is
  (no authToken)                   → no Authorization header (explicit no-auth)

CSV server names always use "\$ANTHROPIC_AUTH_TOKEN" (resolved at write time).

The write script now resolves all "Bearer \$VAR_NAME" placeholders uniformly
from the container environment. Servers with no Authorization header or a
non-placeholder value are written unchanged.

Removes FORWARD_OAUTH_TOKEN global valve — per-server authToken: "\$oauth"
is the opt-in mechanism. The OAuth access_token is always forwarded via header
when available; the backend only embeds it where the config requests it.

Token freshness: on every sub_agent call, ~/.mcp.json is rewritten with the
current access_token, so expiring tokens are refreshed each invocation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…acking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

📝 Walkthrough

Walkthrough

These changes introduce OAuth token support and enhanced MCP server configuration across the system. A new context variable stores MCP OAuth tokens. Configuration handling is extended to accept JSON MCP configs alongside CSV lists, with per-server authentication via headers. OAuth tokens are extracted from request headers and propagated through tool calls.

Changes

Cohort / File(s) Summary
Context Variables
computer-use-server/context_vars.py
Introduces current_mcp_oauth_token context variable initialized with empty-string default to store MCP OAuth tokens in request scope.
MCP Configuration & Docker Management
computer-use-server/docker_manager.py
Updates build_mcp_config to accept either CSV server names or raw JSON MCP config; conditionally injects per-server Authorization headers from OAuth token, environment variable placeholders, or literal auth tokens. Container write script updated to resolve Bearer $VAR_NAME placeholders at runtime.
MCP Request Context Setup
computer-use-server/mcp_tools.py
Adds request header parsing for base64-encoded MCP JSON config (x-mcp-config) and OAuth token (x-mcp-oauth-token). Updates server header documentation and passes OAuth token to build_mcp_config during sub-agent setup.
OpenWebUI Tool Integration
openwebui/tools/computer_use_tools.py
Introduces MCP_SERVERS valve setting for MCP server configuration. Extends _build_mcp_headers and all tool methods (bash_tool, str_replace, create_file, view, sub_agent) to accept and propagate __oauth_token__ parameter through to MCP headers.
Vision API Integration
skills/public/describe-image/scripts/describe.py
Adds conditional user identification to Vision API requests via x-openwebui-user-email header when USER_EMAIL environment variable is non-empty.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server as MCP Server/Tool Handler
    participant Docker as Docker Manager
    participant Container as MCP Container
    participant MCP as MCP Servers

    Client->>Server: Request with x-mcp-oauth-token<br/>& x-mcp-config headers
    Server->>Server: Parse headers:<br/>decode MCP config (JSON),<br/>extract OAuth token
    Server->>Server: Set context variables:<br/>current_mcp_oauth_token,<br/>current_mcp_servers
    Server->>Docker: Call build_mcp_config<br/>with oauth_token
    Docker->>Docker: Parse JSON config or CSV,<br/>inject Authorization headers<br/>with Bearer token/placeholders
    Docker->>Container: Write MCP config<br/>to container
    Container->>Container: Resolve $VAR_NAME<br/>placeholders at runtime
    Server->>Container: Execute tool with<br/>populated MCP headers
    Container->>MCP: Authenticated request<br/>with Authorization header
    MCP-->>Container: Response
    Container-->>Server: Tool result
    Server-->>Client: Response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 OAuth tokens hop through headers bright,
MCP servers configured just right,
JSON configs decode with glee,
Authentication flows so free,
Sub-agents dance in token light! 🪚✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Additional MCP Server functionality' is vague and overly broad—it doesn't specify the main change (MCP OAuth authentication and JSON config support), making it unclear to someone scanning history. Revise to be more specific, such as 'Add OAuth token forwarding and per-server MCP authentication' or 'Support MCP OAuth tokens and JSON-based server configuration'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
computer-use-server/mcp_tools.py (1)

941-941: Nit: or "" is redundant here.

current_mcp_oauth_token is declared with default="", so .get() already returns "" on unset. Compare with the non-redundant user_email or "" on Line 940 where current_user_email defaults to None.

-            current_mcp_oauth_token.get() or "",
+            current_mcp_oauth_token.get(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@computer-use-server/mcp_tools.py` at line 941, Remove the redundant fallback
"or \"\"" after current_mcp_oauth_token.get() — since current_mcp_oauth_token is
declared with default="" its .get() already returns an empty string; change the
argument to use current_mcp_oauth_token.get() (keep the existing user_email or
"" usage for current_user_email which defaults to None).
openwebui/tools/computer_use_tools.py (1)

513-556: Validate JSON config client-side before base64-encoding.

When MCP_SERVERS starts with {, the valve value is blindly base64-encoded and sent as X-Mcp-Config. If an admin saves a malformed JSON in the valve, every tool call silently ships a broken header — the server swallows the decode failure (see related comment in mcp_tools.py:set_context_from_headers) and the sub-agent runs with no MCP servers, with no user-visible signal. A single json.loads(mcp_servers_valve) inside a try/except here would let you either log at configure-time or fall back cleanly.

🔧 Proposed fix
         # Valve-configured MCP servers (takes priority over TOOL_SERVER_CONNECTIONS)
         mcp_servers_valve = (self.valves.MCP_SERVERS or "").strip()
         if mcp_servers_valve:
             if mcp_servers_valve.startswith("{"):
-                # JSON config: base64-encode to avoid header encoding issues
-                headers["X-Mcp-Config"] = _base64.b64encode(mcp_servers_valve.encode()).decode()
-                headers.pop("X-Mcp-Servers", None)
+                # JSON config: validate then base64-encode to avoid header issues
+                try:
+                    json.loads(mcp_servers_valve)
+                    headers["X-Mcp-Config"] = _base64.b64encode(mcp_servers_valve.encode()).decode()
+                    headers.pop("X-Mcp-Servers", None)
+                except json.JSONDecodeError as e:
+                    print(f"[MCP] Invalid MCP_SERVERS JSON valve, ignoring: {e}")
             else:
                 headers["X-Mcp-Servers"] = mcp_servers_valve
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwebui/tools/computer_use_tools.py` around lines 513 - 556, In
_build_mcp_headers validate the valve JSON before base64-encoding: when
mcp_servers_valve startswith("{"), attempt to json.loads(mcp_servers_valve)
inside a try/except; if json.loads succeeds, base64-encode and set
headers["X-Mcp-Config"], removing "X-Mcp-Servers" as before; if it fails, log a
warning (use self.logger.warning or fallback to self.mcp_client.logger) with the
parse error and do NOT set "X-Mcp-Config" (leave any existing "X-Mcp-Servers"
intact) so malformed valve config is ignored client-side.
computer-use-server/docker_manager.py (1)

329-348: Refactor: inline comprehension is hard to reason about; move to a helper executed inside the container.

The nested ternary with walrus assignments (Lines 334-340) is correct but a maintenance hazard — future edits to the placeholder-resolution semantics (flagged in the related build_mcp_config comment) will be painful. Since the python invoked inside the container is unconstrained in length, consider extracting a named function:

🔧 Sketch
return (
    f"python3 -c '"
    f"import json,os,base64;"
    f"c=json.loads(base64.b64decode(\"{config_b64}\"));"
    f"def resolve(h):\n"
    f"  a=h.get(\"Authorization\",\"\")\n"
    f"  if not a.startswith(\"Bearer $\"): return\n"
    f"  v=a[8:]\n"
    f"  if v in os.environ: h[\"Authorization\"]=\"Bearer \"+os.environ[v]\n"
    f"  else: h.pop(\"Authorization\",None)\n"
    f"[resolve(s.get(\"headers\",{{}})) for s in c[\"mcpServers\"].values() if isinstance(s,dict)];"
    ...
)

(Heredoc / python3 <<'PY' would read even better — avoids the nested-quote thicket entirely.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@computer-use-server/docker_manager.py` around lines 329 - 348, The inline
list-comprehension in the generated container Python (the block decoding
config_b64 and mutating c["mcpServers"] using a nested ternary with walrus ops)
is hard to maintain; replace that comprehension with a named helper function
defined inside the same python3 -c string (e.g. def resolve_headers(h): ... )
that encapsulates the Authorization placeholder logic (read a =
h.get("Authorization",""), check startswith("Bearer $"), extract v = a[8:], set
h["Authorization"]="Bearer "+os.environ[v] if present, else pop), then call that
helper for each server in c["mcpServers"].values() (only for dicts) before
dumping c and updating settings.local.json; keep using config_b64, c, and p as
in the current code but remove the complex inline expression and walrus usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@computer-use-server/docker_manager.py`:
- Around line 289-298: The current logic in docker_manager.py that sets
hdrs["Authorization"] = f"Bearer {auth_spec}" conflates env placeholders and
literal tokens starting with "$", which causes build_mcp_config_write_script to
treat literal tokens as placeholders; update the code in the block that handles
auth_spec (the branch that currently sets hdrs["Authorization"]) to either (A)
resolve environment placeholders here (e.g. if auth_spec matches a clear
placeholder pattern like "$env:VAR" replace with os.environ["VAR"] before
setting Authorization) or (B) require/recognize an explicit prefix for env
placeholders (e.g. "$env:") and only treat those as placeholders while treating
other "$..." strings as literal tokens; also update MCP_SERVERS/valve
documentation to note the required placeholder syntax or that env resolution
happens in the orchestrator so admins don't paste literal secrets that start
with "$".

In `@computer-use-server/mcp_tools.py`:
- Around line 1257-1273: The base64 decode block for mcp_config_b64 currently
swallows all exceptions which hides malformed input; update the try/except
around current_mcp_servers.set(_base64.b64decode(mcp_config_b64).decode()) to
catch specific errors (binascii.Error and UnicodeDecodeError) and log a short
descriptive error (e.g., "Failed to decode X-Mcp-Config header") including the
exception message but do NOT log the raw header value; do not re-raise—leave
current_mcp_servers unchanged on failure. Ensure the logged message is emitted
via the module's logger and add a comment linking to build_mcp_config so future
readers know malformed base64 will fall back to CSV parsing.

In `@openwebui/tools/computer_use_tools.py`:
- Line 649: Replace the mutable default {} for the __oauth_token__ (and
__user__) parameters with None and update their type annotations to allow None
(e.g., Optional[dict] or dict | None) in all affected functions: bash_tool,
str_replace, create_file, view, sub_agent, _build_mcp_headers, and _run_tool;
rely on the existing (__oauth_token__ or {}) usage inside
_build_mcp_headers/_run_tool so no other logic changes are needed.

In `@skills/public/describe-image/scripts/describe.py`:
- Around line 153-155: The describe.py code reads USER_EMAIL from env but that
variable is never set by the container runtime, so the x-openwebui-user-email
header is never sent; fix by either setting USER_EMAIL in the container env
where git env vars are injected (add extra_env["USER_EMAIL"] = user_email in
docker_manager.py alongside
GIT_AUTHOR_EMAIL/GIT_COMMITTER_EMAIL/ANTHROPIC_CUSTOM_HEADERS usage from
current_user_email.get()), or change describe.py to fall back to an existing env
var (e.g., check GIT_AUTHOR_EMAIL or GIT_COMMITTER_EMAIL from os.environ and use
that when USER_EMAIL is empty before attaching
headers["x-openwebui-user-email"]).

---

Nitpick comments:
In `@computer-use-server/docker_manager.py`:
- Around line 329-348: The inline list-comprehension in the generated container
Python (the block decoding config_b64 and mutating c["mcpServers"] using a
nested ternary with walrus ops) is hard to maintain; replace that comprehension
with a named helper function defined inside the same python3 -c string (e.g. def
resolve_headers(h): ... ) that encapsulates the Authorization placeholder logic
(read a = h.get("Authorization",""), check startswith("Bearer $"), extract v =
a[8:], set h["Authorization"]="Bearer "+os.environ[v] if present, else pop),
then call that helper for each server in c["mcpServers"].values() (only for
dicts) before dumping c and updating settings.local.json; keep using config_b64,
c, and p as in the current code but remove the complex inline expression and
walrus usage.

In `@computer-use-server/mcp_tools.py`:
- Line 941: Remove the redundant fallback "or \"\"" after
current_mcp_oauth_token.get() — since current_mcp_oauth_token is declared with
default="" its .get() already returns an empty string; change the argument to
use current_mcp_oauth_token.get() (keep the existing user_email or "" usage for
current_user_email which defaults to None).

In `@openwebui/tools/computer_use_tools.py`:
- Around line 513-556: In _build_mcp_headers validate the valve JSON before
base64-encoding: when mcp_servers_valve startswith("{"), attempt to
json.loads(mcp_servers_valve) inside a try/except; if json.loads succeeds,
base64-encode and set headers["X-Mcp-Config"], removing "X-Mcp-Servers" as
before; if it fails, log a warning (use self.logger.warning or fallback to
self.mcp_client.logger) with the parse error and do NOT set "X-Mcp-Config"
(leave any existing "X-Mcp-Servers" intact) so malformed valve config is ignored
client-side.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b117d29-3a73-4a22-b592-d47be2a7801b

📥 Commits

Reviewing files that changed from the base of the PR and between 117620e and 0bee3d2.

📒 Files selected for processing (5)
  • computer-use-server/context_vars.py
  • computer-use-server/docker_manager.py
  • computer-use-server/mcp_tools.py
  • openwebui/tools/computer_use_tools.py
  • skills/public/describe-image/scripts/describe.py

Comment on lines +289 to +298
if "Authorization" in hdrs or auth_spec is None:
continue # explicit header wins; no authToken = explicit no-auth
if auth_spec == "$oauth":
if oauth_token:
hdrs["Authorization"] = f"Bearer {oauth_token}"
# else: oauth not available → leave without Authorization
elif auth_spec:
# "$ENV_VAR" placeholder or literal token
hdrs["Authorization"] = f"Bearer {auth_spec}"
return {"mcpServers": servers}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Literal authToken values starting with $ will be misinterpreted as env placeholders.

Both "$ENV_VAR" and a literal token go through the same branch (Line 297) producing "Bearer <auth_spec>". Downstream, build_mcp_config_write_script unconditionally treats any Authorization starting with "Bearer $" as a placeholder — so a user whose literal OAuth/API token happens to begin with $ (rare but possible for generated secrets) will have their header silently rewritten or removed inside the container.

Consider distinguishing the two forms explicitly, e.g. a $env:VAR prefix, or resolve env vars here in the orchestrator so the write script only handles literal Authorization values:

🔧 Sketch
-            elif auth_spec:
-                # "$ENV_VAR" placeholder or literal token
-                hdrs["Authorization"] = f"Bearer {auth_spec}"
+            elif auth_spec.startswith("$") and len(auth_spec) > 1:
+                # $ENV_VAR placeholder — resolved in container at write time
+                hdrs["Authorization"] = f"Bearer {auth_spec}"
+            elif auth_spec:
+                # Literal token — pre-escape a leading `$` so the write script
+                # does not mistake it for a placeholder.
+                hdrs["Authorization"] = f"Bearer {auth_spec}"
+                # (or mark with a sentinel the write script can recognise)

At minimum, please call this out in the valve/MCP_SERVERS documentation so admins know not to paste literal secrets that start with $.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@computer-use-server/docker_manager.py` around lines 289 - 298, The current
logic in docker_manager.py that sets hdrs["Authorization"] = f"Bearer
{auth_spec}" conflates env placeholders and literal tokens starting with "$",
which causes build_mcp_config_write_script to treat literal tokens as
placeholders; update the code in the block that handles auth_spec (the branch
that currently sets hdrs["Authorization"]) to either (A) resolve environment
placeholders here (e.g. if auth_spec matches a clear placeholder pattern like
"$env:VAR" replace with os.environ["VAR"] before setting Authorization) or (B)
require/recognize an explicit prefix for env placeholders (e.g. "$env:") and
only treat those as placeholders while treating other "$..." strings as literal
tokens; also update MCP_SERVERS/valve documentation to note the required
placeholder syntax or that env resolution happens in the orchestrator so admins
don't paste literal secrets that start with "$".

Comment on lines +1257 to +1273
# MCP config (full JSON, base64-encoded) — takes priority over CSV list
import base64 as _base64
mcp_config_b64 = (
headers.get("x-mcp-config") or headers.get("x-openwebui-mcp-config") or ""
)
if mcp_config_b64:
try:
current_mcp_servers.set(_base64.b64decode(mcp_config_b64).decode())
except Exception:
pass

# OAuth token for MCP server authentication
mcp_oauth = (
headers.get("x-mcp-oauth-token") or headers.get("x-openwebui-mcp-oauth-token") or ""
)
if mcp_oauth:
current_mcp_oauth_token.set(mcp_oauth)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Log decode failures instead of silently swallowing.

If a client sends an X-Mcp-Config header but the base64/JSON is malformed, the except: pass path silently keeps whatever current_mcp_servers CSV was set from x-mcp-servers above — the caller never learns their JSON config was rejected, and worse, the decoded-but-not-JSON-yet text could even be interpreted as a CSV list of server names downstream in build_mcp_config. At minimum log the failure (without echoing the raw token), and prefer catching the specific exceptions to match Ruff S110/BLE001.

🔧 Proposed fix
-    # MCP config (full JSON, base64-encoded) — takes priority over CSV list
-    import base64 as _base64
-    mcp_config_b64 = (
-        headers.get("x-mcp-config") or headers.get("x-openwebui-mcp-config") or ""
-    )
-    if mcp_config_b64:
-        try:
-            current_mcp_servers.set(_base64.b64decode(mcp_config_b64).decode())
-        except Exception:
-            pass
+    # MCP config (full JSON, base64-encoded) — takes priority over CSV list
+    import base64 as _base64
+    import binascii
+    mcp_config_b64 = (
+        headers.get("x-mcp-config") or headers.get("x-openwebui-mcp-config") or ""
+    )
+    if mcp_config_b64:
+        try:
+            current_mcp_servers.set(_base64.b64decode(mcp_config_b64).decode("utf-8"))
+        except (binascii.Error, UnicodeDecodeError) as e:
+            print(f"[MCP] Warning: failed to decode X-Mcp-Config header: {type(e).__name__}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# MCP config (full JSON, base64-encoded) — takes priority over CSV list
import base64 as _base64
mcp_config_b64 = (
headers.get("x-mcp-config") or headers.get("x-openwebui-mcp-config") or ""
)
if mcp_config_b64:
try:
current_mcp_servers.set(_base64.b64decode(mcp_config_b64).decode())
except Exception:
pass
# OAuth token for MCP server authentication
mcp_oauth = (
headers.get("x-mcp-oauth-token") or headers.get("x-openwebui-mcp-oauth-token") or ""
)
if mcp_oauth:
current_mcp_oauth_token.set(mcp_oauth)
# MCP config (full JSON, base64-encoded) — takes priority over CSV list
import base64 as _base64
import binascii
mcp_config_b64 = (
headers.get("x-mcp-config") or headers.get("x-openwebui-mcp-config") or ""
)
if mcp_config_b64:
try:
current_mcp_servers.set(_base64.b64decode(mcp_config_b64).decode("utf-8"))
except (binascii.Error, UnicodeDecodeError) as e:
print(f"[MCP] Warning: failed to decode X-Mcp-Config header: {type(e).__name__}")
# OAuth token for MCP server authentication
mcp_oauth = (
headers.get("x-mcp-oauth-token") or headers.get("x-openwebui-mcp-oauth-token") or ""
)
if mcp_oauth:
current_mcp_oauth_token.set(mcp_oauth)
🧰 Tools
🪛 Ruff (0.15.11)

[error] 1265-1266: try-except-pass detected, consider logging the exception

(S110)


[warning] 1265-1265: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@computer-use-server/mcp_tools.py` around lines 1257 - 1273, The base64 decode
block for mcp_config_b64 currently swallows all exceptions which hides malformed
input; update the try/except around
current_mcp_servers.set(_base64.b64decode(mcp_config_b64).decode()) to catch
specific errors (binascii.Error and UnicodeDecodeError) and log a short
descriptive error (e.g., "Failed to decode X-Mcp-Config header") including the
exception message but do NOT log the raw header value; do not re-raise—leave
current_mcp_servers unchanged on failure. Ensure the logged message is emitted
via the module's logger and add a comment linking to build_mcp_config so future
readers know malformed base64 will fall back to CSV parsing.

__user__: dict = None,
__files__: Optional[List[dict]] = None,
__request__=None,
__oauth_token__: dict = {},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid mutable default {} on __oauth_token__ parameters (Ruff B006).

All five tool wrappers (bash_tool, str_replace, create_file, view, sub_agent) declare __oauth_token__: dict = {}. The shared default object is a classic Python footgun — harmless today because the code never mutates it, but any future edit that does (e.g. adding a field before forwarding) would silently leak state across invocations. _build_mcp_headers already tolerates None via (__oauth_token__ or {}), so the fix is a one-liner per signature:

-        __oauth_token__: dict = {},
+        __oauth_token__: Optional[dict] = None,

Same change applies to the __user__: dict = None / __oauth_token__: dict = None signatures of _build_mcp_headers (Lines 516, 518) and _run_tool (Line 587) where Ruff RUF013 also flags the implicit Optional.

Also applies to: 679-679, 711-711, 740-740, 777-777

🧰 Tools
🪛 Ruff (0.15.11)

[warning] 649-649: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwebui/tools/computer_use_tools.py` at line 649, Replace the mutable
default {} for the __oauth_token__ (and __user__) parameters with None and
update their type annotations to allow None (e.g., Optional[dict] or dict |
None) in all affected functions: bash_tool, str_replace, create_file, view,
sub_agent, _build_mcp_headers, and _run_tool; rely on the existing
(__oauth_token__ or {}) usage inside _build_mcp_headers/_run_tool so no other
logic changes are needed.

Comment on lines +153 to +155
user_email = os.environ.get("USER_EMAIL", "")
if user_email:
headers["x-openwebui-user-email"] = user_email
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for any place USER_EMAIL is set in container env or passed through
rg -nP --type=py -C2 '\bUSER_EMAIL\b'
# Also verify the relevant block in docker_manager.py
fd -t f 'docker_manager.py' --exec rg -nP -C3 'current_user_email|extra_env\[' {}

Repository: Yambr/open-computer-use

Length of output: 5184


🏁 Script executed:

# Check the file header
head -5 skills/public/describe-image/scripts/describe.py

# Search for any other USER_EMAIL assignments
rg -n "USER_EMAIL\s*=" --type=py

# Check if ANTHROPIC_CUSTOM_HEADERS is used anywhere
rg -n "ANTHROPIC_CUSTOM_HEADERS" --type=py

Repository: Yambr/open-computer-use

Length of output: 1126


USER_EMAIL is never set in the container environment — header will not be forwarded.

Per computer-use-server/docker_manager.py line 538, the container environment receives GIT_AUTHOR_EMAIL, GIT_COMMITTER_EMAIL, and ANTHROPIC_CUSTOM_HEADERS from current_user_email.get(). There is no USER_EMAIL being exported, so os.environ.get("USER_EMAIL", "") will always return an empty string and the x-openwebui-user-email header will never be attached to the Vision API request. This defeats the PR's stated purpose.

Fix by either:

  1. Adding extra_env["USER_EMAIL"] = user_email in docker_manager.py alongside the existing git env vars, or
  2. Falling back to an env var that is already populated:
Option 2 (script-only fix)
-    user_email = os.environ.get("USER_EMAIL", "")
+    user_email = os.environ.get("USER_EMAIL") or os.environ.get("GIT_AUTHOR_EMAIL", "")
     if user_email:
         headers["x-openwebui-user-email"] = user_email
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/public/describe-image/scripts/describe.py` around lines 153 - 155, The
describe.py code reads USER_EMAIL from env but that variable is never set by the
container runtime, so the x-openwebui-user-email header is never sent; fix by
either setting USER_EMAIL in the container env where git env vars are injected
(add extra_env["USER_EMAIL"] = user_email in docker_manager.py alongside
GIT_AUTHOR_EMAIL/GIT_COMMITTER_EMAIL/ANTHROPIC_CUSTOM_HEADERS usage from
current_user_email.get()), or change describe.py to fall back to an existing env
var (e.g., check GIT_AUTHOR_EMAIL or GIT_COMMITTER_EMAIL from os.environ and use
that when USER_EMAIL is empty before attaching
headers["x-openwebui-user-email"]).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants