diff --git a/src/mcp_server_appwrite/http_app.py b/src/mcp_server_appwrite/http_app.py index 6a3bc5c..dc12351 100644 --- a/src/mcp_server_appwrite/http_app.py +++ b/src/mcp_server_appwrite/http_app.py @@ -13,6 +13,7 @@ following ``resource_metadata`` from the 401 challenge. Mirrors the Appwrite authorization server's discovery document verbatim. * ``/healthz`` — liveness probe. +* ``/.well-known/openai-apps-challenge`` — public domain verification token. Auth uses the SDK primitives (``BearerAuthBackend`` + ``AuthContextMiddleware``) so the validated token is reachable from tool handlers via ``get_access_token()``. @@ -438,6 +439,11 @@ async def health_endpoint(request: Request) -> PlainTextResponse: return PlainTextResponse(f"appwrite-mcp {SERVER_VERSION} ok") +async def openai_apps_challenge_endpoint(request: Request) -> PlainTextResponse: + """Serve the public domain-ownership challenge for the OpenAI app listing.""" + return PlainTextResponse("Fv03Ea1-vV7p7oIpvL3y2bRKrxVBJnSmscrDOdsVRuk") + + async def favicon_svg_endpoint(request: Request) -> Response: return Response( _icon_svg(), @@ -477,6 +483,11 @@ async def lifespan(app: Starlette): yield routes = [ + Route( + "/.well-known/openai-apps-challenge", + endpoint=openai_apps_challenge_endpoint, + methods=["GET"], + ), Route( "/.well-known/oauth-protected-resource/mcp", endpoint=mcp_path_protected_resource_metadata_endpoint, diff --git a/tests/unit/test_http_app.py b/tests/unit/test_http_app.py index 0d09129..8a0b723 100644 --- a/tests/unit/test_http_app.py +++ b/tests/unit/test_http_app.py @@ -8,6 +8,7 @@ from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import InMemoryMetricReader +from starlette.testclient import TestClient from mcp_server_appwrite import auth, telemetry from mcp_server_appwrite.http_app import ( @@ -317,6 +318,24 @@ def test_app_routes_include_legacy_discovery_paths(self): self.assertIn("/.well-known/oauth-protected-resource", paths) self.assertIn("/.well-known/oauth-authorization-server", paths) + def test_openai_challenge_is_public_plain_text(self): + from mcp_server_appwrite import server as server_module + + original_transport = server_module._UPLOAD_TRANSPORT + self.addCleanup(setattr, server_module, "_UPLOAD_TRANSPORT", original_transport) + + with TestClient(build_app()) as client: + response = client.get( + "/.well-known/openai-apps-challenge", follow_redirects=False + ) + + self.assertEqual(response.status_code, 200) + self.assertEqual( + response.content, b"Fv03Ea1-vV7p7oIpvL3y2bRKrxVBJnSmscrDOdsVRuk" + ) + media_type = response.headers["content-type"].split(";", 1)[0].strip() + self.assertEqual(media_type, "text/plain") + class ConsoleOverrideTests(unittest.TestCase): """The MCP_CONSOLE_URL tester flag: discovery rewrites plus the local