Skip to content

issue: OAuth authentication with static client metadata #272

Description

@bpellens

Check Existing Issues

  • I have searched the existing issues and discussions.
  • I am using the latest version of mcpo.

mcpo Version

0.0.19

Open WebUI Version (if applicable)

No response

Operating System

macOS Sequoia

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both MCPO and Open WebUI.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have listed steps to reproduce the bug in detail.

Expected Behavior

A web browser would open initiating the OAuth flow.

Actual Behavior

Still a dynamic client registration seems to be performed. No OAuth flow is started.

Steps to Reproduce

  1. Configure mcpo using the following static client metadata (as Google does not support dynamic client registration)

{ "mcpServers": { "toolbox-mcp": { "type": "streamable-http", "url": "http://127.0.0.1:5000/mcp", "oauth": { "server_url": "https://accounts.google.com", "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth", "token_endpoint": "https://oauth2.googleapis.com/token", "storage_type": "file", "use_loopback": true, "callback_port": 3030, "client_metadata": { "client_name": "MySampleApp", "client_id": "101******.apps.googleusercontent.com", "client_secret": "GO******", "redirect_uris": ["http://localhost:3030/callback"], "scope": "openid email profile" } } } } }

  1. Launch the mcpo procy, using the above configuration, via: uvx mcpo@0.0.19 --config mcpoconfig.json

  2. Add the tool to Open WebUI

Image
  1. Trigger the tool.

Logs & Screenshots

Starting MCP OpenAPI Proxy with config file: mcpoconfig.json
2025-11-21 09:42:51,101 - INFO - Starting MCPO Server...
2025-11-21 09:42:51,101 - INFO - Name: MCP OpenAPI Proxy
2025-11-21 09:42:51,101 - INFO - Version: 1.0
2025-11-21 09:42:51,101 - INFO - Description: Automatically generated API from MCP Tool Schemas
2025-11-21 09:42:51,101 - INFO - Hostname: ******.local
2025-11-21 09:42:51,101 - INFO - Port: 8000
2025-11-21 09:42:51,101 - INFO - API Key: Not Provided
2025-11-21 09:42:51,101 - INFO - CORS Allowed Origins: ['
']
2025-11-21 09:42:51,102 - INFO - Path Prefix: /
2025-11-21 09:42:51,102 - INFO - Root Path:
2025-11-21 09:42:51,102 - INFO - Loading MCP server configurations from: mcpoconfig.json
2025-11-21 09:42:51,102 - INFO - Configuring MCP Servers:
2025-11-21 09:42:51,102 - INFO - Uvicorn server starting...
INFO: Started server process [22387]
INFO: Waiting for application startup.
2025-11-21 09:42:51,109 - INFO - Initiating connection for server: 'toolbox-mcp'...
2025-11-21 09:42:51,109 - INFO - OAuth configuration detected for server: toolbox-mcp
2025-11-21 09:42:51,112 - INFO - OAuth provider created for server: toolbox-mcp
2025-11-21 09:42:51,129 - INFO - HTTP Request: POST http://127.0.0.1:5000/mcp "HTTP/1.1 200 OK"
2025-11-21 09:42:51,129 - INFO - Negotiated protocol version: 2025-06-18
2025-11-21 09:42:51,130 - INFO - HTTP Request: POST http://127.0.0.1:5000/mcp "HTTP/1.1 202 Accepted"
2025-11-21 09:42:51,131 - INFO - HTTP Request: POST http://127.0.0.1:5000/mcp "HTTP/1.1 200 OK"
2025-11-21 09:42:51,132 - INFO - Successfully connected to 'toolbox-mcp'.
2025-11-21 09:42:51,132 - INFO -
--- Server Startup Summary ---
2025-11-21 09:42:51,132 - INFO - Successfully connected to:
2025-11-21 09:42:51,132 - INFO - - toolbox-mcp
2025-11-21 09:42:51,132 - INFO - --------------------------

INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: 127.0.0.1:59644 - "OPTIONS /toolbox-mcp/openapi.json HTTP/1.1" 200 OK
INFO: 127.0.0.1:59644 - "GET /toolbox-mcp/openapi.json HTTP/1.1" 200 OK
INFO: 127.0.0.1:59644 - "GET /toolbox-mcp/openapi.json HTTP/1.1" 200 OK
INFO: 127.0.0.1:59644 - "GET /toolbox-mcp/openapi.json HTTP/1.1" 200 OK
INFO: 127.0.0.1:59696 - "OPTIONS /toolbox-mcp/find-profiles-by-skill HTTP/1.1" 200 OK
2025-11-21 09:51:35,787 - INFO - Calling endpoint: find-profiles-by-skill, with args: {'skillName': 'C#'}
2025-11-21 09:51:35,794 - INFO - HTTP Request: POST http://127.0.0.1:5000/mcp "HTTP/1.1 401 Unauthorized"
2025-11-21 09:51:35,909 - INFO - HTTP Request: GET https://accounts.google.com/.well-known/oauth-protected-resource "HTTP/1.1 404 Not Found"
2025-11-21 09:51:35,931 - INFO - HTTP Request: GET https://accounts.google.com/.well-known/oauth-authorization-server "HTTP/1.1 200 OK"
2025-11-21 09:51:35,961 - INFO - HTTP Request: POST https://accounts.google.com/register "HTTP/1.1 400 Bad Request"
2025-11-21 09:51:35,962 - ERROR - OAuth flow error
Traceback (most recent call last):
File "/Users//.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/auth/oauth2.py", line 522, in async_auth_flow
client_information = await handle_registration_response(registration_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/
/.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/auth/utils.py", line 234, in handle_registration_response
raise OAuthRegistrationError(f"Registration failed: {response.status_code} {response.text}")
mcp.client.auth.exceptions.OAuthRegistrationError: Registration failed: 400 <title>Error 400 (Bad Request)!!1</title><style nonce="jrmkwMbw87vCPldJ4EGIvQ">{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{color:#222;text-align:unset;margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px;} > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}pre{white-space:pre-wrap;}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}</style>

400. That’s an error.

The server cannot process the request because it is malformed. It should not be retried. That’s all we know.


2025-11-21 09:51:35,969 - ERROR - Failed to connect to MCP server 'Toolbox': ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)

  • Exception Group Traceback (most recent call last):
    | File "/Users//.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcpo/main.py", line 567, in lifespan
    | async with client_context as (reader, writer, _):
    | ^^^^^^^^^^^^^^
    | File "/opt/homebrew/Cellar/python@3.13/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 235, in aexit
    | await self.gen.athrow(value)
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/streamable_http.py", line 480, in streamablehttp_client
    | async with anyio.create_task_group() as tg:
    | ~~~~~~~~~~~~~~~~~~~~~~~^^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 781, in aexit
    | raise BaseExceptionGroup(
    | "unhandled errors in a TaskGroup", self._exceptions
    | ) from None
    | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
    +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/streamable_http.py", line 411, in handle_request_async
    | await self._handle_post_request(ctx)
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/streamable_http.py", line 262, in _handle_post_request
    | async with ctx.client.stream(
    | ~~~~~~~~~~~~~~~~~^
    | "POST",
    | ^^^^^^^
    | ...<2 lines>...
    | headers=headers,
    | ^^^^^^^^^^^^^^^^
    | ) as response:
    | ^
    | File "/opt/homebrew/Cellar/python@3.13/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 214, in aenter
    | return await anext(self.gen)
    | ^^^^^^^^^^^^^^^^^^^^^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/httpx/_client.py", line 1583, in stream
    | response = await self.send(
    | ^^^^^^^^^^^^^^^^
    | ...<4 lines>...
    | )
    | ^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/httpx/_client.py", line 1629, in send
    | response = await self._send_handling_auth(
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | ...<4 lines>...
    | )
    | ^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/httpx/_client.py", line 1675, in _send_handling_auth
    | raise exc
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/httpx/_client.py", line 1664, in _send_handling_auth
    | next_request = await auth_flow.asend(response)
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/auth/oauth2.py", line 522, in async_auth_flow
    | client_information = await handle_registration_response(registration_response)
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/auth/utils.py", line 234, in handle_registration_response
    | raise OAuthRegistrationError(f"Registration failed: {response.status_code} {response.text}")
    | mcp.client.auth.exceptions.OAuthRegistrationError: Registration failed: 400 <title>Error 400 (Bad Request)!!1</title><style nonce="jrmkwMbw87vCPldJ4EGIvQ">
    {margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:Some type hints lead to MCPO not running #222;padding:15px}body{color:Some type hints lead to MCPO not running #222;text-align:unset;margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px;}
    > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}pre{white-space:pre-wrap;}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}</style>

    400. That’s an error.

    The server cannot process the request because it is malformed. It should not be retried. That’s all we know.


    +------------------------------------
    ERROR: + Exception Group Traceback (most recent call last):
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/starlette/routing.py", line 694, in lifespan
    | async with self.lifespan_context(app) as maybe_state:
    | ~~~~~~~~~~~~~~~~~~~~~^^^^^
    | File "/opt/homebrew/Cellar/python@3.13/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 235, in aexit
    | await self.gen.athrow(value)
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcpo/main.py", line 438, in lifespan
    | async with AsyncExitStack() as stack:
    | ~~~~~~~~~~~~~~^^
    | File "/opt/homebrew/Cellar/python@3.13/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 768, in aexit
    | raise exc
    | File "/opt/homebrew/Cellar/python@3.13/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 751, in aexit
    | cb_suppress = await cb(exc_details)
    | ^^^^^^^^^^^^^^^^^^^^^^
    | File "/opt/homebrew/Cellar/python@3.13/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 235, in aexit
    | await self.gen.athrow(value)
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcpo/main.py", line 567, in lifespan
    | async with client_context as (reader, writer, _):
    | ^^^^^^^^^^^^^^
    | File "/opt/homebrew/Cellar/python@3.13/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 235, in aexit
    | await self.gen.athrow(value)
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/streamable_http.py", line 480, in streamablehttp_client
    | async with anyio.create_task_group() as tg:
    | ~~~~~~~~~~~~~~~~~~~~~~~^^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 781, in aexit
    | raise BaseExceptionGroup(
    | "unhandled errors in a TaskGroup", self._exceptions
    | ) from None
    | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
    +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/streamable_http.py", line 411, in handle_request_async
    | await self._handle_post_request(ctx)
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/streamable_http.py", line 262, in _handle_post_request
    | async with ctx.client.stream(
    | ~~~~~~~~~~~~~~~~~^
    | "POST",
    | ^^^^^^^
    | ...<2 lines>...
    | headers=headers,
    | ^^^^^^^^^^^^^^^^
    | ) as response:
    | ^
    | File "/opt/homebrew/Cellar/python@3.13/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 214, in aenter
    | return await anext(self.gen)
    | ^^^^^^^^^^^^^^^^^^^^^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/httpx/_client.py", line 1583, in stream
    | response = await self.send(
    | ^^^^^^^^^^^^^^^^
    | ...<4 lines>...
    | )
    | ^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/httpx/_client.py", line 1629, in send
    | response = await self._send_handling_auth(
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | ...<4 lines>...
    | )
    | ^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/httpx/_client.py", line 1675, in _send_handling_auth
    | raise exc
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/httpx/_client.py", line 1664, in _send_handling_auth
    | next_request = await auth_flow.asend(response)
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | File "/Users/
    /.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/auth/oauth2.py", line 522, in async_auth_flow
    | client_information = await handle_registration_response(registration_response)
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | File "/Users/
    */.cache/uv/archive-v0/LCGc1p2kkXKfMqNpTMiyL/lib/python3.13/site-packages/mcp/client/auth/utils.py", line 234, in handle_registration_response
    | raise OAuthRegistrationError(f"Registration failed: {response.status_code} {response.text}")
    | mcp.client.auth.exceptions.OAuthRegistrationError: Registration failed: 400 <title>Error 400 (Bad Request)!!1</title><style nonce="jrmkwMbw87vCPldJ4EGIvQ">
    {margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:Some type hints lead to MCPO not running #222;padding:15px}body{color:Some type hints lead to MCPO not running #222;text-align:unset;margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px;}
    > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}pre{white-space:pre-wrap;}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}</style>

    400. That’s an error.

    The server cannot process the request because it is malformed. It should not be retried. That’s all we know.


    +------------------------------------

Additional Information

My objective is to expose my MCP server, which is created with the MCP Toolbox for Databases (https://googleapis.github.io/genai-toolbox), as a tool in my Open WebUI via the mcpo proxy. The MCP server is configured with a BigQuery source and tool. I would like to pass the user context to the tool, executing the query on my BigQuery dataset.

As Google does not seem to support dynamic OAuth client registration, I would like to configure the mcpo proxy using static client metadata. I expected with this configuration that a browser based OAuth flow would be initiated. However, if I understand correctly, the mcpo still tries to initiate a dynamic client registration and consequently fails.

Is it possible to setup the mcpo proxy in such a way that a user-based OAuth flow is triggered and the access token resulting from the OAuth flow is passed to the MCP server?
(My apologies if this question, or similar has been raised elsewhere).

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Fields

No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions