Additional MCP Server functionality#72
Conversation
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>
📝 WalkthroughWalkthroughThese 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
computer-use-server/mcp_tools.py (1)
941-941: Nit:or ""is redundant here.
current_mcp_oauth_tokenis declared withdefault="", so.get()already returns""on unset. Compare with the non-redundantuser_email or ""on Line 940 wherecurrent_user_emaildefaults toNone.- 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_SERVERSstarts with{, the valve value is blindly base64-encoded and sent asX-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 inmcp_tools.py:set_context_from_headers) and the sub-agent runs with no MCP servers, with no user-visible signal. A singlejson.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_configcomment) 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
📒 Files selected for processing (5)
computer-use-server/context_vars.pycomputer-use-server/docker_manager.pycomputer-use-server/mcp_tools.pyopenwebui/tools/computer_use_tools.pyskills/public/describe-image/scripts/describe.py
| 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} |
There was a problem hiding this comment.
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 "$".
| # 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) |
There was a problem hiding this comment.
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.
| # 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 = {}, |
There was a problem hiding this comment.
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.
| user_email = os.environ.get("USER_EMAIL", "") | ||
| if user_email: | ||
| headers["x-openwebui-user-email"] = user_email |
There was a problem hiding this comment.
🧩 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=pyRepository: 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:
- Adding
extra_env["USER_EMAIL"] = user_emailindocker_manager.pyalongside the existing git env vars, or - 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"]).
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
Improvements