golf-mcp 0.1.19__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of golf-mcp might be problematic. Click here for more details.
- golf/__init__.py +9 -1
- golf/_endpoints.py +6 -0
- golf/_endpoints_fallback.py +10 -0
- golf/auth/__init__.py +188 -84
- golf/auth/api_key.py +6 -14
- golf/auth/factory.py +333 -0
- golf/auth/helpers.py +12 -42
- golf/auth/providers.py +396 -0
- golf/auth/registry.py +256 -0
- golf/cli/branding.py +192 -0
- golf/cli/main.py +28 -69
- golf/commands/__init__.py +2 -0
- golf/commands/build.py +4 -7
- golf/commands/init.py +30 -53
- golf/commands/run.py +50 -20
- golf/core/builder.py +356 -412
- golf/core/builder_auth.py +63 -144
- golf/core/builder_telemetry.py +26 -3
- golf/core/config.py +38 -59
- golf/core/parser.py +132 -139
- golf/core/platform.py +12 -10
- golf/core/telemetry.py +11 -19
- golf/core/transformer.py +38 -15
- golf/examples/__pycache__/__init__.cpython-311.pyc +0 -0
- golf/examples/basic/.coverage +0 -0
- golf/examples/basic/.env.example +8 -4
- golf/examples/basic/README.md +117 -45
- golf/examples/basic/__pycache__/auth.cpython-311.pyc +0 -0
- golf/examples/basic/auth.py +76 -0
- golf/examples/basic/golf.json +2 -5
- golf/examples/basic/htmlcov/.gitignore +2 -0
- golf/examples/basic/htmlcov/class_index.html +547 -0
- golf/examples/basic/htmlcov/coverage_html_cb_6fb7b396.js +733 -0
- golf/examples/basic/htmlcov/favicon_32_cb_58284776.png +0 -0
- golf/examples/basic/htmlcov/function_index.html +2091 -0
- golf/examples/basic/htmlcov/index.html +349 -0
- golf/examples/basic/htmlcov/keybd_closed_cb_ce680311.png +0 -0
- golf/examples/basic/htmlcov/status.json +1 -0
- golf/examples/basic/htmlcov/style_cb_8e611ae1.css +337 -0
- golf/examples/basic/htmlcov/z_1c9a91c0e91c8496___init___py.html +323 -0
- golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_api_key_py.html +170 -0
- golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_factory_py.html +430 -0
- golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_helpers_py.html +288 -0
- golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_providers_py.html +493 -0
- golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_registry_py.html +353 -0
- golf/examples/basic/htmlcov/z_3ec3b3f490dc0950___init___py.html +120 -0
- golf/examples/basic/htmlcov/z_3ec3b3f490dc0950_instrumentation_py.html +1535 -0
- golf/examples/basic/htmlcov/z_4b8b9dd4ccccc5db___init___py.html +98 -0
- golf/examples/basic/htmlcov/z_4b8b9dd4ccccc5db_branding_py.html +289 -0
- golf/examples/basic/htmlcov/z_4b8b9dd4ccccc5db_main_py.html +476 -0
- golf/examples/basic/htmlcov/z_5a6c4e6bcc86fb2f___init___py.html +97 -0
- golf/examples/basic/htmlcov/z_6cadab9ec0df475d___init___py.html +102 -0
- golf/examples/basic/htmlcov/z_6cadab9ec0df475d_build_py.html +178 -0
- golf/examples/basic/htmlcov/z_6cadab9ec0df475d_init_py.html +387 -0
- golf/examples/basic/htmlcov/z_6cadab9ec0df475d_run_py.html +222 -0
- golf/examples/basic/htmlcov/z_6fcdee0582ba84e4___init___py.html +106 -0
- golf/examples/basic/htmlcov/z_6fcdee0582ba84e4__endpoints_fallback_py.html +107 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217___init___py.html +98 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_auth_py.html +306 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_metrics_py.html +329 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_py.html +1471 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_telemetry_py.html +186 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217_config_py.html +315 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217_parser_py.html +1149 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217_platform_py.html +279 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217_telemetry_py.html +589 -0
- golf/examples/basic/htmlcov/z_7ba499ed22986217_transformer_py.html +286 -0
- golf/examples/basic/htmlcov/z_7d7da37693a43688___init___py.html +107 -0
- golf/examples/basic/htmlcov/z_7d7da37693a43688_collector_py.html +417 -0
- golf/examples/basic/htmlcov/z_7d7da37693a43688_registry_py.html +109 -0
- golf/examples/basic/htmlcov/z_abe733142b40ad4e___init___py.html +109 -0
- golf/examples/basic/htmlcov/z_abe733142b40ad4e_context_py.html +150 -0
- golf/examples/basic/htmlcov/z_abe733142b40ad4e_elicitation_py.html +267 -0
- golf/examples/basic/htmlcov/z_abe733142b40ad4e_sampling_py.html +318 -0
- golf/examples/basic/prompts/__pycache__/welcome.cpython-311.pyc +0 -0
- golf/examples/basic/prompts/welcome.py +3 -5
- golf/examples/basic/resources/__pycache__/current_time.cpython-311.pyc +0 -0
- golf/examples/basic/resources/__pycache__/info.cpython-311.pyc +0 -0
- golf/examples/basic/resources/current_time.py +5 -13
- golf/examples/basic/resources/weather/__pycache__/common.cpython-311.pyc +0 -0
- golf/examples/basic/resources/weather/__pycache__/current.cpython-311.pyc +0 -0
- golf/examples/basic/resources/weather/__pycache__/forecast.cpython-311.pyc +0 -0
- golf/examples/basic/resources/weather/city.py +46 -0
- golf/examples/basic/resources/weather/common.py +4 -11
- golf/examples/basic/resources/weather/current.py +5 -5
- golf/examples/basic/resources/weather/forecast.py +5 -5
- golf/examples/basic/tools/__pycache__/calculator.cpython-311.pyc +0 -0
- golf/examples/basic/tools/calculator.py +94 -0
- golf/examples/basic/tools/say/__pycache__/hello.cpython-311.pyc +0 -0
- golf/examples/basic/tools/say/hello.py +65 -0
- golf/metrics/collector.py +100 -19
- golf/telemetry/__init__.py +4 -0
- golf/telemetry/instrumentation.py +496 -174
- golf/utilities/__init__.py +12 -0
- golf/utilities/context.py +53 -0
- golf/utilities/elicitation.py +170 -0
- golf/utilities/sampling.py +221 -0
- {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/METADATA +56 -110
- golf_mcp-0.2.0.dist-info/RECORD +110 -0
- golf/auth/oauth.py +0 -861
- golf/auth/provider.py +0 -115
- golf/examples/api_key/.env +0 -2
- golf/examples/api_key/.env.example +0 -1
- golf/examples/api_key/README.md +0 -84
- golf/examples/api_key/golf.json +0 -8
- golf/examples/api_key/pre_build.py +0 -11
- golf/examples/api_key/tools/issues/create.py +0 -93
- golf/examples/api_key/tools/issues/list.py +0 -92
- golf/examples/api_key/tools/repos/list.py +0 -111
- golf/examples/api_key/tools/search/code.py +0 -106
- golf/examples/api_key/tools/users/get.py +0 -82
- golf/examples/basic/.env +0 -5
- golf/examples/basic/pre_build.py +0 -28
- golf/examples/basic/tools/github_user.py +0 -65
- golf/examples/basic/tools/hello.py +0 -34
- golf/examples/basic/tools/payments/charge.py +0 -70
- golf/examples/basic/tools/payments/common.py +0 -36
- golf/examples/basic/tools/payments/refund.py +0 -61
- golf_mcp-0.1.19.dist-info/RECORD +0 -60
- {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/WHEEL +0 -0
- {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
- {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/top_level.txt +0 -0
golf/auth/factory.py
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"""Factory functions for creating FastMCP authentication providers."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
# Import these at runtime to avoid import errors during Golf installation
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from fastmcp.server.auth.auth import AuthProvider
|
|
11
|
+
from fastmcp.server.auth import JWTVerifier, StaticTokenVerifier
|
|
12
|
+
from mcp.server.auth.settings import RevocationOptions
|
|
13
|
+
|
|
14
|
+
from .providers import (
|
|
15
|
+
AuthConfig,
|
|
16
|
+
JWTAuthConfig,
|
|
17
|
+
StaticTokenConfig,
|
|
18
|
+
OAuthServerConfig,
|
|
19
|
+
RemoteAuthConfig,
|
|
20
|
+
)
|
|
21
|
+
from .registry import (
|
|
22
|
+
get_provider_registry,
|
|
23
|
+
create_auth_provider_from_registry,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def create_auth_provider(config: AuthConfig) -> "AuthProvider":
|
|
28
|
+
"""Create a FastMCP AuthProvider from Golf auth configuration.
|
|
29
|
+
|
|
30
|
+
This function uses the provider registry system to allow extensibility.
|
|
31
|
+
Built-in providers are automatically registered, and custom providers
|
|
32
|
+
can be added via the registry system.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
config: Golf authentication configuration
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Configured FastMCP AuthProvider instance
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: If configuration is invalid
|
|
42
|
+
ImportError: If required dependencies are missing
|
|
43
|
+
KeyError: If provider type is not registered
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
return create_auth_provider_from_registry(config)
|
|
47
|
+
except KeyError:
|
|
48
|
+
# Fall back to legacy dispatch for backward compatibility
|
|
49
|
+
# This ensures existing code continues to work during transition
|
|
50
|
+
if config.provider_type == "jwt":
|
|
51
|
+
return _create_jwt_provider(config)
|
|
52
|
+
elif config.provider_type == "static":
|
|
53
|
+
return _create_static_provider(config)
|
|
54
|
+
elif config.provider_type == "oauth_server":
|
|
55
|
+
return _create_oauth_server_provider(config)
|
|
56
|
+
elif config.provider_type == "remote":
|
|
57
|
+
return _create_remote_provider(config)
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError(f"Unknown provider type: {config.provider_type}") from None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _create_jwt_provider(config: JWTAuthConfig) -> "JWTVerifier":
|
|
63
|
+
"""Create JWT token verifier from configuration."""
|
|
64
|
+
# Resolve runtime values from environment variables
|
|
65
|
+
public_key = config.public_key
|
|
66
|
+
if config.public_key_env_var:
|
|
67
|
+
env_value = os.environ.get(config.public_key_env_var)
|
|
68
|
+
if env_value:
|
|
69
|
+
public_key = env_value
|
|
70
|
+
|
|
71
|
+
jwks_uri = config.jwks_uri
|
|
72
|
+
if config.jwks_uri_env_var:
|
|
73
|
+
env_value = os.environ.get(config.jwks_uri_env_var)
|
|
74
|
+
if env_value:
|
|
75
|
+
jwks_uri = env_value
|
|
76
|
+
|
|
77
|
+
issuer = config.issuer
|
|
78
|
+
if config.issuer_env_var:
|
|
79
|
+
env_value = os.environ.get(config.issuer_env_var)
|
|
80
|
+
if env_value:
|
|
81
|
+
issuer = env_value
|
|
82
|
+
|
|
83
|
+
audience = config.audience
|
|
84
|
+
if config.audience_env_var:
|
|
85
|
+
env_value = os.environ.get(config.audience_env_var)
|
|
86
|
+
if env_value:
|
|
87
|
+
# Handle both string and comma-separated list
|
|
88
|
+
if "," in env_value:
|
|
89
|
+
audience = [s.strip() for s in env_value.split(",")]
|
|
90
|
+
else:
|
|
91
|
+
audience = env_value
|
|
92
|
+
|
|
93
|
+
# Validate configuration
|
|
94
|
+
if not public_key and not jwks_uri:
|
|
95
|
+
raise ValueError("Either public_key or jwks_uri must be provided for JWT verification")
|
|
96
|
+
|
|
97
|
+
if public_key and jwks_uri:
|
|
98
|
+
raise ValueError("Provide either public_key or jwks_uri, not both")
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
from fastmcp.server.auth import JWTVerifier
|
|
102
|
+
except ImportError as e:
|
|
103
|
+
raise ImportError("JWTVerifier not available. Please install fastmcp>=2.11.0") from e
|
|
104
|
+
|
|
105
|
+
return JWTVerifier(
|
|
106
|
+
public_key=public_key,
|
|
107
|
+
jwks_uri=jwks_uri,
|
|
108
|
+
issuer=issuer,
|
|
109
|
+
audience=audience,
|
|
110
|
+
algorithm=config.algorithm,
|
|
111
|
+
required_scopes=config.required_scopes,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _create_static_provider(config: StaticTokenConfig) -> "StaticTokenVerifier":
|
|
116
|
+
"""Create static token verifier from configuration."""
|
|
117
|
+
if not config.tokens:
|
|
118
|
+
raise ValueError("Static token provider requires at least one token")
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
from fastmcp.server.auth import StaticTokenVerifier
|
|
122
|
+
except ImportError as e:
|
|
123
|
+
raise ImportError("StaticTokenVerifier not available. Please install fastmcp>=2.11.0") from e
|
|
124
|
+
|
|
125
|
+
return StaticTokenVerifier(
|
|
126
|
+
tokens=config.tokens,
|
|
127
|
+
required_scopes=config.required_scopes,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _create_oauth_server_provider(config: OAuthServerConfig) -> "AuthProvider":
|
|
132
|
+
"""Create OAuth authorization server provider from configuration."""
|
|
133
|
+
try:
|
|
134
|
+
from fastmcp.server.auth import OAuthProvider
|
|
135
|
+
except ImportError as e:
|
|
136
|
+
raise ImportError(
|
|
137
|
+
"OAuthProvider not available in this FastMCP version. Please upgrade to FastMCP 2.11.0 or later."
|
|
138
|
+
) from e
|
|
139
|
+
|
|
140
|
+
# Resolve runtime values from environment variables with validation
|
|
141
|
+
base_url = config.base_url
|
|
142
|
+
if config.base_url_env_var:
|
|
143
|
+
env_value = os.environ.get(config.base_url_env_var)
|
|
144
|
+
if env_value:
|
|
145
|
+
# Apply the same validation as the config field to env var value
|
|
146
|
+
try:
|
|
147
|
+
from urllib.parse import urlparse
|
|
148
|
+
|
|
149
|
+
env_value = env_value.strip()
|
|
150
|
+
parsed = urlparse(env_value)
|
|
151
|
+
|
|
152
|
+
if not parsed.scheme or not parsed.netloc:
|
|
153
|
+
raise ValueError(
|
|
154
|
+
f"Invalid base URL from environment variable {config.base_url_env_var}: '{env_value}'"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if parsed.scheme not in ("http", "https"):
|
|
158
|
+
raise ValueError(f"Base URL from environment must use http/https: '{env_value}'")
|
|
159
|
+
|
|
160
|
+
# Production HTTPS check
|
|
161
|
+
is_production = (
|
|
162
|
+
os.environ.get("GOLF_ENV", "").lower() in ("prod", "production")
|
|
163
|
+
or os.environ.get("NODE_ENV", "").lower() == "production"
|
|
164
|
+
or os.environ.get("ENVIRONMENT", "").lower() in ("prod", "production")
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if is_production and parsed.scheme == "http":
|
|
168
|
+
raise ValueError(f"Base URL must use HTTPS in production: '{env_value}'")
|
|
169
|
+
|
|
170
|
+
base_url = env_value
|
|
171
|
+
|
|
172
|
+
except Exception as e:
|
|
173
|
+
raise ValueError(f"Invalid base URL from environment variable {config.base_url_env_var}: {e}") from e
|
|
174
|
+
|
|
175
|
+
# Additional security validations before creating provider
|
|
176
|
+
from urllib.parse import urlparse
|
|
177
|
+
|
|
178
|
+
# Validate final base_url
|
|
179
|
+
parsed_base = urlparse(base_url)
|
|
180
|
+
if not parsed_base.scheme or not parsed_base.netloc:
|
|
181
|
+
raise ValueError(f"Invalid base URL: '{base_url}'")
|
|
182
|
+
|
|
183
|
+
# Security check: prevent localhost in production
|
|
184
|
+
is_production = (
|
|
185
|
+
os.environ.get("GOLF_ENV", "").lower() in ("prod", "production")
|
|
186
|
+
or os.environ.get("NODE_ENV", "").lower() == "production"
|
|
187
|
+
or os.environ.get("ENVIRONMENT", "").lower() in ("prod", "production")
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if is_production and parsed_base.hostname in ("localhost", "127.0.0.1", "0.0.0.0"):
|
|
191
|
+
raise ValueError(f"Cannot use localhost/loopback addresses in production: '{base_url}'")
|
|
192
|
+
|
|
193
|
+
# Client registration options - always disabled for security
|
|
194
|
+
client_reg_options = None
|
|
195
|
+
|
|
196
|
+
# Create revocation options
|
|
197
|
+
revocation_options = None
|
|
198
|
+
if config.allow_token_revocation:
|
|
199
|
+
revocation_options = RevocationOptions(enabled=True)
|
|
200
|
+
|
|
201
|
+
return OAuthProvider(
|
|
202
|
+
base_url=base_url,
|
|
203
|
+
issuer_url=config.issuer_url,
|
|
204
|
+
service_documentation_url=config.service_documentation_url,
|
|
205
|
+
client_registration_options=client_reg_options,
|
|
206
|
+
revocation_options=revocation_options,
|
|
207
|
+
required_scopes=config.required_scopes,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _create_remote_provider(config: RemoteAuthConfig) -> "AuthProvider":
|
|
212
|
+
"""Create remote auth provider from configuration."""
|
|
213
|
+
try:
|
|
214
|
+
from fastmcp.server.auth import RemoteAuthProvider
|
|
215
|
+
except ImportError as e:
|
|
216
|
+
raise ImportError(
|
|
217
|
+
"RemoteAuthProvider not available in this FastMCP version. Please upgrade to FastMCP 2.11.0 or later."
|
|
218
|
+
) from e
|
|
219
|
+
|
|
220
|
+
# Resolve runtime values from environment variables
|
|
221
|
+
authorization_servers = config.authorization_servers
|
|
222
|
+
if config.authorization_servers_env_var:
|
|
223
|
+
env_value = os.environ.get(config.authorization_servers_env_var)
|
|
224
|
+
if env_value:
|
|
225
|
+
# Split comma-separated values and strip whitespace
|
|
226
|
+
authorization_servers = [s.strip() for s in env_value.split(",")]
|
|
227
|
+
|
|
228
|
+
resource_server_url = config.resource_server_url
|
|
229
|
+
if config.resource_server_url_env_var:
|
|
230
|
+
env_value = os.environ.get(config.resource_server_url_env_var)
|
|
231
|
+
if env_value:
|
|
232
|
+
resource_server_url = env_value
|
|
233
|
+
|
|
234
|
+
# Create the underlying token verifier
|
|
235
|
+
token_verifier = create_auth_provider(config.token_verifier_config)
|
|
236
|
+
|
|
237
|
+
# Ensure it's actually a TokenVerifier
|
|
238
|
+
if not hasattr(token_verifier, "verify_token"):
|
|
239
|
+
raise ValueError(f"Remote auth provider requires a TokenVerifier, got {type(token_verifier).__name__}")
|
|
240
|
+
|
|
241
|
+
return RemoteAuthProvider(
|
|
242
|
+
token_verifier=token_verifier,
|
|
243
|
+
authorization_servers=authorization_servers,
|
|
244
|
+
resource_server_url=resource_server_url,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def create_simple_jwt_provider(
|
|
249
|
+
*,
|
|
250
|
+
jwks_uri: str | None = None,
|
|
251
|
+
public_key: str | None = None,
|
|
252
|
+
issuer: str | None = None,
|
|
253
|
+
audience: str | list[str] | None = None,
|
|
254
|
+
required_scopes: list[str] | None = None,
|
|
255
|
+
) -> "JWTVerifier":
|
|
256
|
+
"""Create a simple JWT provider for common use cases.
|
|
257
|
+
|
|
258
|
+
This is a convenience function for creating JWT providers without
|
|
259
|
+
having to construct the full configuration objects.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
jwks_uri: JWKS URI for key fetching
|
|
263
|
+
public_key: Static public key (PEM format)
|
|
264
|
+
issuer: Expected issuer claim
|
|
265
|
+
audience: Expected audience claim(s)
|
|
266
|
+
required_scopes: Required scopes for all requests
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Configured JWTVerifier instance
|
|
270
|
+
"""
|
|
271
|
+
config = JWTAuthConfig(
|
|
272
|
+
jwks_uri=jwks_uri,
|
|
273
|
+
public_key=public_key,
|
|
274
|
+
issuer=issuer,
|
|
275
|
+
audience=audience,
|
|
276
|
+
required_scopes=required_scopes or [],
|
|
277
|
+
)
|
|
278
|
+
return _create_jwt_provider(config)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def create_dev_token_provider(
|
|
282
|
+
tokens: dict[str, Any] | None = None,
|
|
283
|
+
required_scopes: list[str] | None = None,
|
|
284
|
+
) -> "StaticTokenVerifier":
|
|
285
|
+
"""Create a static token provider for development.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
tokens: Token dictionary or None for default dev tokens
|
|
289
|
+
required_scopes: Required scopes for all requests
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Configured StaticTokenVerifier instance
|
|
293
|
+
"""
|
|
294
|
+
if tokens is None:
|
|
295
|
+
# Default development tokens
|
|
296
|
+
tokens = {
|
|
297
|
+
"dev-token-123": {
|
|
298
|
+
"client_id": "dev-client",
|
|
299
|
+
"scopes": ["read", "write"],
|
|
300
|
+
},
|
|
301
|
+
"admin-token-456": {
|
|
302
|
+
"client_id": "admin-client",
|
|
303
|
+
"scopes": ["read", "write", "admin"],
|
|
304
|
+
},
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
config = StaticTokenConfig(
|
|
308
|
+
tokens=tokens,
|
|
309
|
+
required_scopes=required_scopes or [],
|
|
310
|
+
)
|
|
311
|
+
return _create_static_provider(config)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def register_builtin_providers() -> None:
|
|
315
|
+
"""Register built-in authentication providers in the registry.
|
|
316
|
+
|
|
317
|
+
This function registers the standard Golf authentication providers:
|
|
318
|
+
- jwt: JWT token verification
|
|
319
|
+
- static: Static token verification (development)
|
|
320
|
+
- oauth_server: Full OAuth authorization server
|
|
321
|
+
- remote: Remote authorization server integration
|
|
322
|
+
"""
|
|
323
|
+
registry = get_provider_registry()
|
|
324
|
+
|
|
325
|
+
# Register built-in provider factories
|
|
326
|
+
registry.register_factory("jwt", _create_jwt_provider)
|
|
327
|
+
registry.register_factory("static", _create_static_provider)
|
|
328
|
+
registry.register_factory("oauth_server", _create_oauth_server_provider)
|
|
329
|
+
registry.register_factory("remote", _create_remote_provider)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
# Register built-in providers when module is imported
|
|
333
|
+
register_builtin_providers()
|
golf/auth/helpers.py
CHANGED
|
@@ -3,48 +3,22 @@
|
|
|
3
3
|
from contextvars import ContextVar
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
from mcp.server.auth.middleware.auth_context import get_access_token
|
|
8
|
-
|
|
9
|
-
from .oauth import GolfOAuthProvider
|
|
10
|
-
|
|
11
|
-
# Context variable to store the active OAuth provider
|
|
12
|
-
_active_golf_oauth_provider: GolfOAuthProvider | None = None
|
|
6
|
+
from starlette.requests import Request
|
|
13
7
|
|
|
14
8
|
# Context variable to store the current request's API key
|
|
15
9
|
_current_api_key: ContextVar[str | None] = ContextVar("current_api_key", default=None)
|
|
16
10
|
|
|
17
11
|
|
|
18
|
-
def _set_active_golf_oauth_provider(provider: GolfOAuthProvider) -> None:
|
|
19
|
-
"""
|
|
20
|
-
Sets the active GolfOAuthProvider instance.
|
|
21
|
-
Should only be called once during server startup.
|
|
22
|
-
"""
|
|
23
|
-
global _active_golf_oauth_provider
|
|
24
|
-
_active_golf_oauth_provider = provider
|
|
25
|
-
|
|
26
|
-
|
|
27
12
|
def get_provider_token() -> str | None:
|
|
28
13
|
"""
|
|
29
|
-
Get a provider token (
|
|
30
|
-
MCP session's access token.
|
|
14
|
+
Get a provider token (legacy function - no longer supported in Golf 0.2.x).
|
|
31
15
|
|
|
32
|
-
|
|
16
|
+
In Golf 0.2.x, use FastMCP's built-in auth providers for OAuth flows.
|
|
17
|
+
This function returns None and is kept for backwards compatibility.
|
|
33
18
|
"""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return None
|
|
38
|
-
|
|
39
|
-
provider = _active_golf_oauth_provider
|
|
40
|
-
if not provider:
|
|
41
|
-
return None
|
|
42
|
-
|
|
43
|
-
if not hasattr(provider, "get_provider_token"):
|
|
44
|
-
return None
|
|
45
|
-
|
|
46
|
-
# Call the get_provider_token method on the actual GolfOAuthProvider instance
|
|
47
|
-
return provider.get_provider_token(mcp_access_token.token)
|
|
19
|
+
# Legacy OAuth provider support removed in Golf 0.2.x
|
|
20
|
+
# Use FastMCP 2.11+ auth providers instead
|
|
21
|
+
return None
|
|
48
22
|
|
|
49
23
|
|
|
50
24
|
def extract_token_from_header(auth_header: str) -> str | None:
|
|
@@ -149,7 +123,7 @@ def get_api_key() -> str | None:
|
|
|
149
123
|
return None
|
|
150
124
|
|
|
151
125
|
|
|
152
|
-
def get_api_key_from_request(request) -> str | None:
|
|
126
|
+
def get_api_key_from_request(request: Request) -> str | None:
|
|
153
127
|
"""Get the API key from a specific request object.
|
|
154
128
|
|
|
155
129
|
This is useful when you have direct access to the request object.
|
|
@@ -199,23 +173,19 @@ def debug_api_key_context() -> dict[str, Any]:
|
|
|
199
173
|
if task:
|
|
200
174
|
debug_info["has_async_task"] = True
|
|
201
175
|
debug_info["task_id"] = id(task)
|
|
202
|
-
except:
|
|
176
|
+
except Exception:
|
|
203
177
|
pass
|
|
204
178
|
|
|
205
179
|
try:
|
|
206
180
|
main_module = sys.modules.get("__main__")
|
|
207
181
|
if main_module:
|
|
208
|
-
debug_info["main_module_has_storage"] = hasattr(
|
|
209
|
-
|
|
210
|
-
)
|
|
211
|
-
debug_info["main_module_has_context"] = hasattr(
|
|
212
|
-
main_module, "request_id_context"
|
|
213
|
-
)
|
|
182
|
+
debug_info["main_module_has_storage"] = hasattr(main_module, "api_key_storage")
|
|
183
|
+
debug_info["main_module_has_context"] = hasattr(main_module, "request_id_context")
|
|
214
184
|
|
|
215
185
|
if hasattr(main_module, "request_id_context"):
|
|
216
186
|
request_id_context = main_module.request_id_context
|
|
217
187
|
debug_info["request_id_from_context"] = request_id_context.get()
|
|
218
|
-
except:
|
|
188
|
+
except Exception:
|
|
219
189
|
pass
|
|
220
190
|
|
|
221
191
|
return debug_info
|