Refactor to hosted HTTP + OAuth 2.1 transport#42
Draft
ChiragAgg5k wants to merge 5 commits into
Draft
Conversation
Replace the stdio + API-key server with a hosted, multi-tenant OAuth 2.1
resource server over the MCP Streamable HTTP transport. Users authenticate
against Appwrite Cloud's per-project OAuth server; the MCP validates the
bearer token and forwards it to the Appwrite REST API (which accepts the
OAuth2 access token directly).
- Transport: custom Starlette ASGI app serving Streamable HTTP at
/{project_id}/mcp (SSE responses, stateless). stdio transport removed.
- Auth (new auth.py): RFC 9728 protected-resource metadata, WWW-Authenticate
401 challenge, and a per-project RS256/JWKS token verifier that derives the
project from the JWT `iss` claim and enforces RFC 8707 audience binding.
- Execution: split tool schema (built once via SDK introspection) from
binding; each call re-binds the SDK method to a per-request client built
from the request's OAuth token.
- Packaging: Dockerfile, streamable-http remote in server.json, HTTP/OAuth
README, .env.example. Bump to 0.5.0.
- Tooling: add Ruff linter (E/F/W/I) alongside black; pin GitHub Actions to
commit SHAs, pin uv (0.11.22) and build/twine; add Docker-build CI job.
API-key helpers remain only as integration-test scaffolding.
- register_services now auto-discovers every Appwrite SDK service class (14 services, 376 tools) via an EXCLUDED_SERVICES knob, instead of a hardcoded list of 9. Adds account, databases, graphql, health, tokens. - protected-resource metadata's scopes_supported is now sourced live from the project's authorization-server discovery (cached per project), so it never drifts from the tool surface. Removed the static scope list.
Auto-discovery picks up the new oauth2, apps, and 9 other services (25 total, 575 tools). SDK 21.0.0 returns Pydantic models whose to_dict() keeps result formatting intact, and stores the project in client config rather than eagerly in headers (test assertion updated). Bumps server version to 0.6.0.
The cloud OAuth server now advertises an open registration_endpoint (RFC 7591), so MCP clients self-register without pre-shared credentials. This server is the OAuth 2.1 Resource Server and needs no runtime change; document the now-live self-service registration step and lock the discovery contract it depends on. - README: explicit self-service DCR step (public/PKCE, no client_id/secret to provision), project-setup note, and regional APPWRITE_ENDPOINT caveat (token iss is validated against it). - unit: supported_scopes works against DCR-enabled discovery (no network). - integration: assert the AS advertises registration_endpoint == issuer/register and exposes scopes_supported; skips when the endpoint predates DCR.
Collapse the multi-tenant /<project_id>/mcp routing to a single-tenant /mcp endpoint for the Appwrite Cloud console project. The served project is now a constant (APPWRITE_PROJECT_ID, default 'console'); the token verifier rejects any token whose issuer names a different project. Also fix the README service undercount (14 -> 25 auto-discovered services) and remove the project-setup and self-hosting sections, since the OAuth endpoints only exist on Cloud.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the stdio + API-key MCP with a hosted, multi-tenant OAuth 2.1 resource server over the MCP Streamable HTTP transport. Users authenticate against Appwrite Cloud's per-project OAuth server; the MCP validates the bearer token and forwards it to the Appwrite REST API, which accepts the OAuth2 access token directly (
AccessToken::findIdentity). This follows the MCP authorization spec (OAuth 2.1 + PKCE, RFC 9728 protected-resource metadata, RFC 8414/OIDC AS discovery, RFC 7591 DCR, RFC 8707 resource indicators).Connection URL is per-project:
https://<host>/<project_id>/mcp.Changes
http_app.py) serving Streamable HTTP at/{project_id}/mcp(SSE responses, stateless). stdio transport,--transport/MCP_TRANSPORTremoved.auth.py) — RFC 9728 protected-resource metadata route,WWW-Authenticate401 challenge, and a per-project RS256/JWKSTokenVerifierthat derives the project from the JWTissclaim and enforces RFC 8707 audience binding. Reuses the SDK'sBearerAuthBackend/AuthContextMiddlewareso the token reaches tool handlers viaget_access_token().resolve_client/build_client_for_request). Theappwrite_search_tools/appwrite_call_tooloperator surface and write-confirmation guard are unchanged.Dockerfile,streamable-httpremote inserver.json, rewritten HTTP/OAuthREADME,.env.example. Version →0.5.0.E/F/W/I,E501left to black) + CI lint step; pinned all GitHub Actions to commit SHAs; pinned uv (0.11.22) andbuild/twine; added a Docker-build CI job.API-key helpers (
build_client, etc.) remain only as integration-test scaffolding — the running server has no API-key path.Testing
test_auth.py);ruff checkandblack --checkclean./healthz200, correct RFC 9728 metadata, and an unauthenticated401+WWW-Authenticatechallenge.Cloud-side dependencies (required for the full live OAuth round-trip)
These live in the Appwrite Cloud repo, not here:
resourceinto the issued JWTaud(the verifier requires audience binding).oAuth2Server.scopesmust include the advertised scope set.Notes
python:3.12-slimtag andruns-on: ubuntu-latest.UP/B) to avoid churn; easy to expand later.