Skip to content

fix(backend): serve path-suffixed MCP OAuth protected-resource metadata (#8506)#8507

Open
kodjima33 wants to merge 3 commits into
mainfrom
watchdog/issue-8506-mcp-oauth-metadata-alias
Open

fix(backend): serve path-suffixed MCP OAuth protected-resource metadata (#8506)#8507
kodjima33 wants to merge 3 commits into
mainfrom
watchdog/issue-8506-mcp-oauth-metadata-alias

Conversation

@kodjima33

@kodjima33 kodjima33 commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Bug (#8506)

ChatGPT Apps review rejected the Omi MCP app at the OAuth/sign-in step. Prod logs show ChatGPT's verifier probing the path-suffixed OAuth Protected Resource Metadata URL for the submitted MCP resource and getting a 404:

404 GET https://api.omi.me/.well-known/oauth-protected-resource/v1/mcp/sse
200 GET https://api.omi.me/.well-known/oauth-protected-resource

The submitted MCP resource is https://api.omi.me/v1/mcp/sse. The credentials/account are fine (issue confirms the full token + tool flow works) — the blocker is metadata discovery.

Root cause

backend/routers/mcp_sse.py only served the root /.well-known/oauth-protected-resource. Per RFC 9728, a client derives the metadata URL by inserting the resource's path after the well-known prefix, i.e. /.well-known/oauth-protected-resource/v1/mcp/sse. That route didn't exist → 404 → ChatGPT's verifier failed discovery.

Fix

  • Extract the metadata document into a small _protected_resource_metadata() helper.
  • Keep the existing root route serving it.
  • Add an alias route GET /.well-known/oauth-protected-resource/v1/mcp/sse returning the same document.

This is a pure public-discovery metadata route — no change to token validation, credential handling, or signing. The deeper OAuth-flow hardening items raised in the issue's audit (real authorization-code flow, PKCE enforcement, redirect-URI allowlisting, scope enforcement, RFC-shaped /token responses) touch the credential/token path and are intentionally left for a human-owned change — out of this watchdog's scope.

Tests

New backend/tests/unit/test_mcp_oauth_metadata.py (registered in test.sh):

  • root and path-suffixed endpoints return identical metadata,
  • canonical resource == https://api.omi.me/v1/mcp/sse,
  • both routes are registered.

py_compile + scripts/lint_async_blockers.py clean; pre-commit black clean. Runtime UNVERIFIED locally (no fastapi/pytest in this env); CI runs the suite.

🤖 automated by hourly watchdog; opened for review, not merged.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 3 files

Confidence score: 5/5

  • In backend/tests/unit/test_mcp_oauth_metadata.py, the missing _drop_stale_module cleanup helper can let stale auto-mocked models.* modules leak between test states, which may cause flaky or misleading unit test outcomes rather than product behavior regressions; this looks low-risk for runtime code, but add the helper and corresponding calls before merge to keep the test isolation pattern consistent.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/tests/unit/test_mcp_oauth_metadata.py">

<violation number="1" location="backend/tests/unit/test_mcp_oauth_metadata.py:50">
P3: Missing `_drop_stale_module` helper — the test claims to follow the pattern from test_mcp_data_endpoints.py but omits this function and its calls. Without it, stale auto-mock versions of `models.memories` and `models.conversation_enums` could remain cached from prior imports, causing the real-module import at `from routers import mcp_sse` to silently fail or resolve to the wrong object in certain local-dev scenarios.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic


_ensure_package_path('utils', os.path.join(_BACKEND_DIR, 'utils'))
_ensure_package_path('utils.retrieval', os.path.join(_BACKEND_DIR, 'utils', 'retrieval'))
_ensure_package_path('models', os.path.join(_BACKEND_DIR, 'models'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Missing _drop_stale_module helper — the test claims to follow the pattern from test_mcp_data_endpoints.py but omits this function and its calls. Without it, stale auto-mock versions of models.memories and models.conversation_enums could remain cached from prior imports, causing the real-module import at from routers import mcp_sse to silently fail or resolve to the wrong object in certain local-dev scenarios.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/tests/unit/test_mcp_oauth_metadata.py, line 50:

<comment>Missing `_drop_stale_module` helper — the test claims to follow the pattern from test_mcp_data_endpoints.py but omits this function and its calls. Without it, stale auto-mock versions of `models.memories` and `models.conversation_enums` could remain cached from prior imports, causing the real-module import at `from routers import mcp_sse` to silently fail or resolve to the wrong object in certain local-dev scenarios.</comment>

<file context>
@@ -0,0 +1,133 @@
+
+_ensure_package_path('utils', os.path.join(_BACKEND_DIR, 'utils'))
+_ensure_package_path('utils.retrieval', os.path.join(_BACKEND_DIR, 'utils', 'retrieval'))
+_ensure_package_path('models', os.path.join(_BACKEND_DIR, 'models'))
+
+_stubs = [
</file context>

@Git-on-my-level Git-on-my-level added security-review Needs security/privacy review needs-maintainer-review Needs a human maintainer to review/approve (e.g. stacked, product, or architecture judgment) labels Jun 28, 2026
@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks for the focused fix here. I reviewed the diff and ran the new unit test in an isolated worktree with stripped env.

Positive signal from automation: the added path-suffixed protected-resource metadata route returns the same document as the existing root route, the canonical resource remains https://api.omi.me/v1/mcp/sse, and the targeted test passes (3 passed). This looks like a reasonable narrow fix for the observed ChatGPT verifier 404.

Because this touches OAuth/MCP discovery and is tied to the broader #8506 auth-review flow, I’m not formally approving from automation. Please have a human maintainer do the final security/product check before merge, especially to confirm this stopgap is acceptable while the larger OAuth 2.1 work remains open.

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

Labels

needs-maintainer-review Needs a human maintainer to review/approve (e.g. stacked, product, or architecture judgment) security-review Needs security/privacy review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants