Fix MCP OAuth for servers that publish their endpoint as the resource

Linear and Sentry publish https://host/mcp as their protected resource.
DocsGPTOAuth gave the MCP SDK only the server's origin, so the SDK's
resource check (RFC 8707) failed before sign-in with "Protected resource
https://mcp.linear.app/mcp does not match expected
https://mcp.linear.app". The provider now gets the full MCP URL; servers
that publish their origin still pass, and tokens stay keyed by origin.
This commit is contained in:
arc53-machine committed 2026-09-28 22:57:54 +01:00
1 parent 79fd2ac6cd
commit a9390ec39e
2 files changed
+27 -1

No files matched your search

+5 -1
View File
@@ -752,8 +752,12 @@ class DocsGPTOAuth(OAuthClientProvider):
connection_id=connection_id,
)
# The SDK checks the server's protected-resource metadata against a
# resource derived from this URL, so it gets the full MCP endpoint:
# Linear and Sentry publish ``https://host/mcp``, and an origin-only
# URL fails that check before sign-in. Tokens stay keyed by origin.
super().__init__(
server_url=self.server_base_url,
server_url=mcp_url.rstrip("/") or self.server_base_url,
client_metadata=client_metadata,
storage=storage,
redirect_handler=self.redirect_handler,
+22
View File
@@ -120,3 +120,25 @@ class TestDiscover:
with pytest.raises(service.ConnectionUnavailable), patch("docsgpt.agents.tools.mcp_tool.MCPTool") as tool_cls:
mcp._discover("alice", connection, {"config": {}})
tool_cls.assert_not_called()
def test_oauth_resource_is_the_full_mcp_endpoint():
"""Linear and Sentry publish ``https://host/mcp`` as their protected
resource; the SDK must be given that endpoint, not only the origin."""
from mcp.shared.auth_utils import check_resource_allowed, resource_url_from_server_url
from docsgpt.agents.tools.mcp_tool import DocsGPTOAuth
oauth = DocsGPTOAuth(
mcp_url="https://mcp.linear.app/mcp",
redis_client=MagicMock(),
redirect_uri="https://example.com/callback",
user_id="user1",
)
assert oauth.context.server_url == "https://mcp.linear.app/mcp"
requested = resource_url_from_server_url(oauth.context.server_url)
# Servers that publish the endpoint, and those that publish the origin.
assert check_resource_allowed(requested_resource=requested, configured_resource="https://mcp.linear.app/mcp")
assert check_resource_allowed(requested_resource=requested, configured_resource="https://mcp.linear.app")
# Stored tokens stay keyed by the server's origin.
assert oauth.context.storage.server_url == "https://mcp.linear.app"