diff --git a/src/mcp/oauth/metadata.py b/src/mcp/oauth/metadata.py new file mode 100644 index 0000000..89a74f1 --- /dev/null +++ b/src/mcp/oauth/metadata.py @@ -0,0 +1,31 @@ +SCOPES = ["mcp", "offline_access"] + + +def mcp_resource(base_url: str) -> str: + return f"{base_url.rstrip('/')}/_mcp" + + +def protected_resource_metadata(base_url: str) -> dict: + base = base_url.rstrip("/") + return { + "resource": mcp_resource(base), + "authorization_servers": [base], + "scopes_supported": SCOPES, + "bearer_methods_supported": ["header"], + } + + +def authorization_server_metadata(base_url: str) -> dict: + base = base_url.rstrip("/") + return { + "issuer": base, + "authorization_endpoint": f"{base}/oauth/authorize", + "token_endpoint": f"{base}/oauth/token", + "registration_endpoint": f"{base}/oauth/register", + "revocation_endpoint": f"{base}/oauth/revoke", + "scopes_supported": SCOPES, + "response_types_supported": ["code"], + "grant_types_supported": ["authorization_code", "refresh_token"], + "token_endpoint_auth_methods_supported": ["none"], + "code_challenge_methods_supported": ["S256"], + } diff --git a/tests/mcp/test_oauth_metadata.py b/tests/mcp/test_oauth_metadata.py new file mode 100644 index 0000000..16085b0 --- /dev/null +++ b/tests/mcp/test_oauth_metadata.py @@ -0,0 +1,22 @@ +from src.mcp.oauth import metadata + +BASE = "https://colibre.fr" + + +def test_protected_resource_metadata(): + m = metadata.protected_resource_metadata(BASE) + assert m["resource"] == "https://colibre.fr/_mcp" + assert m["authorization_servers"] == ["https://colibre.fr"] + assert "offline_access" in m["scopes_supported"] + + +def test_authorization_server_metadata(): + m = metadata.authorization_server_metadata(BASE) + assert m["issuer"] == "https://colibre.fr" + assert m["authorization_endpoint"] == "https://colibre.fr/oauth/authorize" + assert m["token_endpoint"] == "https://colibre.fr/oauth/token" + assert m["registration_endpoint"] == "https://colibre.fr/oauth/register" + assert m["code_challenge_methods_supported"] == ["S256"] + assert m["token_endpoint_auth_methods_supported"] == ["none"] + assert set(m["grant_types_supported"]) == {"authorization_code", "refresh_token"} + assert "offline_access" in m["scopes_supported"]