golf-mcp 0.2.1__tar.gz → 0.2.3__tar.gz
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_mcp-0.2.1 → golf_mcp-0.2.3}/MANIFEST.in +1 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/PKG-INFO +1 -1
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/pyproject.toml +2 -2
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/__init__.py +1 -1
- golf_mcp-0.2.3/src/golf/_endpoints.py.in +6 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/auth/__init__.py +4 -4
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/auth/factory.py +4 -2
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/auth/providers.py +169 -1
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/builder_auth.py +2 -2
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf_mcp.egg-info/PKG-INFO +1 -1
- golf_mcp-0.2.3/src/golf_mcp.egg-info/SOURCES.txt +63 -0
- golf_mcp-0.2.1/src/golf/examples/basic/.coverage +0 -0
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/.gitignore +0 -2
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/class_index.html +0 -547
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/coverage_html_cb_6fb7b396.js +0 -733
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/favicon_32_cb_58284776.png +0 -0
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/function_index.html +0 -2091
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/index.html +0 -349
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/keybd_closed_cb_ce680311.png +0 -0
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/status.json +0 -1
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/style_cb_8e611ae1.css +0 -337
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_1c9a91c0e91c8496___init___py.html +0 -323
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_api_key_py.html +0 -170
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_factory_py.html +0 -430
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_helpers_py.html +0 -288
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_providers_py.html +0 -493
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_registry_py.html +0 -353
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_3ec3b3f490dc0950___init___py.html +0 -120
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_3ec3b3f490dc0950_instrumentation_py.html +0 -1535
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_4b8b9dd4ccccc5db___init___py.html +0 -98
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_4b8b9dd4ccccc5db_branding_py.html +0 -289
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_4b8b9dd4ccccc5db_main_py.html +0 -476
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_5a6c4e6bcc86fb2f___init___py.html +0 -97
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_6cadab9ec0df475d___init___py.html +0 -102
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_6cadab9ec0df475d_build_py.html +0 -178
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_6cadab9ec0df475d_init_py.html +0 -387
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_6cadab9ec0df475d_run_py.html +0 -222
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_6fcdee0582ba84e4___init___py.html +0 -106
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_6fcdee0582ba84e4__endpoints_fallback_py.html +0 -107
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217___init___py.html +0 -98
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_auth_py.html +0 -306
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_metrics_py.html +0 -329
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_py.html +0 -1471
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_telemetry_py.html +0 -186
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217_config_py.html +0 -315
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217_parser_py.html +0 -1149
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217_platform_py.html +0 -279
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217_telemetry_py.html +0 -589
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7ba499ed22986217_transformer_py.html +0 -286
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7d7da37693a43688___init___py.html +0 -107
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7d7da37693a43688_collector_py.html +0 -417
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_7d7da37693a43688_registry_py.html +0 -109
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_abe733142b40ad4e___init___py.html +0 -109
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_abe733142b40ad4e_context_py.html +0 -150
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_abe733142b40ad4e_elicitation_py.html +0 -267
- golf_mcp-0.2.1/src/golf/examples/basic/htmlcov/z_abe733142b40ad4e_sampling_py.html +0 -318
- golf_mcp-0.2.1/src/golf_mcp.egg-info/SOURCES.txt +0 -107
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/.docs/docs.md +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/.docs/fastmcp-diff.md +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/.docs/mcp.md +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/LICENSE +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/README.md +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/setup.cfg +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/setup.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/_endpoints_fallback.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/auth/api_key.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/auth/helpers.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/auth/registry.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/cli/__init__.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/cli/branding.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/cli/main.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/commands/__init__.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/commands/build.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/commands/init.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/commands/run.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/__init__.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/builder.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/builder_metrics.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/builder_telemetry.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/config.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/parser.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/platform.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/telemetry.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/core/transformer.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/__init__.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/.env.example +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/README.md +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/auth.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/golf.json +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/prompts/welcome.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/resources/current_time.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/resources/info.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/resources/weather/city.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/resources/weather/common.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/resources/weather/current.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/resources/weather/forecast.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/tools/calculator.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/examples/basic/tools/say/hello.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/metrics/__init__.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/metrics/collector.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/metrics/registry.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/telemetry/__init__.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/telemetry/instrumentation.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/utilities/__init__.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/utilities/context.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/utilities/elicitation.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf/utilities/sampling.py +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf_mcp.egg-info/dependency_links.txt +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf_mcp.egg-info/entry_points.txt +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf_mcp.egg-info/requires.txt +0 -0
- {golf_mcp-0.2.1 → golf_mcp-0.2.3}/src/golf_mcp.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "golf-mcp"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Framework for building MCP servers"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Antoni Gmitruk", email = "antoni@golf.dev"}
|
|
@@ -66,7 +66,7 @@ golf = ["examples/**/*"]
|
|
|
66
66
|
|
|
67
67
|
[tool.poetry]
|
|
68
68
|
name = "golf-mcp"
|
|
69
|
-
version = "0.2.
|
|
69
|
+
version = "0.2.3"
|
|
70
70
|
description = "Framework for building MCP servers with zero boilerplate"
|
|
71
71
|
authors = ["Antoni Gmitruk <antoni@golf.dev>"]
|
|
72
72
|
license = "Apache-2.0"
|
|
@@ -206,14 +206,14 @@ def configure_oauth_proxy(
|
|
|
206
206
|
redirect_path: str = "/oauth/callback",
|
|
207
207
|
) -> None:
|
|
208
208
|
"""Configure OAuth proxy authentication for non-DCR providers.
|
|
209
|
-
|
|
209
|
+
|
|
210
210
|
This sets up an OAuth proxy that bridges MCP clients (expecting DCR) with
|
|
211
211
|
traditional OAuth providers like GitHub, Google, Okta Web Apps that use
|
|
212
212
|
fixed client credentials.
|
|
213
|
-
|
|
213
|
+
|
|
214
214
|
Args:
|
|
215
215
|
upstream_authorization_endpoint: Provider's authorization URL
|
|
216
|
-
upstream_token_endpoint: Provider's token endpoint URL
|
|
216
|
+
upstream_token_endpoint: Provider's token endpoint URL
|
|
217
217
|
upstream_client_id: Your client ID registered with the provider
|
|
218
218
|
upstream_client_secret: Your client secret from the provider
|
|
219
219
|
base_url: This proxy server's public URL
|
|
@@ -221,7 +221,7 @@ def configure_oauth_proxy(
|
|
|
221
221
|
scopes_supported: Scopes to advertise to MCP clients
|
|
222
222
|
upstream_revocation_endpoint: Optional token revocation endpoint
|
|
223
223
|
redirect_path: OAuth callback path (default: /oauth/callback)
|
|
224
|
-
|
|
224
|
+
|
|
225
225
|
Note:
|
|
226
226
|
Requires golf-mcp-enterprise package for implementation.
|
|
227
227
|
"""
|
|
@@ -258,6 +258,7 @@ def _create_oauth_proxy_provider(config: OAuthProxyConfig) -> "AuthProvider":
|
|
|
258
258
|
try:
|
|
259
259
|
# Try to import from enterprise package
|
|
260
260
|
from golf_enterprise import create_oauth_proxy_provider
|
|
261
|
+
|
|
261
262
|
return create_oauth_proxy_provider(config)
|
|
262
263
|
except ImportError as e:
|
|
263
264
|
raise ImportError(
|
|
@@ -342,7 +343,8 @@ def register_builtin_providers() -> None:
|
|
|
342
343
|
- static: Static token verification (development)
|
|
343
344
|
- oauth_server: Full OAuth authorization server
|
|
344
345
|
- remote: Remote authorization server integration
|
|
345
|
-
|
|
346
|
+
|
|
347
|
+
Note: oauth_proxy provider is registered by the golf-mcp-enterprise package
|
|
346
348
|
"""
|
|
347
349
|
registry = get_provider_registry()
|
|
348
350
|
|
|
@@ -351,7 +353,7 @@ def register_builtin_providers() -> None:
|
|
|
351
353
|
registry.register_factory("static", _create_static_provider)
|
|
352
354
|
registry.register_factory("oauth_server", _create_oauth_server_provider)
|
|
353
355
|
registry.register_factory("remote", _create_remote_provider)
|
|
354
|
-
|
|
356
|
+
# oauth_proxy is registered by golf-mcp-enterprise package when installed
|
|
355
357
|
|
|
356
358
|
|
|
357
359
|
# Register built-in providers when module is imported
|
|
@@ -442,5 +442,173 @@ class RemoteAuthConfig(BaseModel):
|
|
|
442
442
|
return self
|
|
443
443
|
|
|
444
444
|
|
|
445
|
+
class OAuthProxyConfig(BaseModel):
|
|
446
|
+
"""Configuration for OAuth proxy functionality (requires golf-mcp-enterprise).
|
|
447
|
+
|
|
448
|
+
This configuration enables bridging MCP clients (which expect Dynamic Client
|
|
449
|
+
Registration) with OAuth providers that use fixed client credentials like
|
|
450
|
+
GitHub Apps, Google Cloud Console apps, Okta Web Applications, etc.
|
|
451
|
+
|
|
452
|
+
The proxy acts as a DCR-capable authorization server to MCP clients while
|
|
453
|
+
using your fixed upstream client credentials with the actual OAuth provider.
|
|
454
|
+
|
|
455
|
+
Note: This class provides configuration only. The actual implementation
|
|
456
|
+
requires the golf-mcp-enterprise package.
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
provider_type: Literal["oauth_proxy"] = "oauth_proxy"
|
|
460
|
+
|
|
461
|
+
# Upstream OAuth provider configuration
|
|
462
|
+
upstream_authorization_endpoint: str = Field(..., description="Upstream provider's authorization endpoint URL")
|
|
463
|
+
upstream_token_endpoint: str = Field(..., description="Upstream provider's token endpoint URL")
|
|
464
|
+
upstream_client_id: str = Field(..., description="Your registered client ID with the upstream provider")
|
|
465
|
+
upstream_client_secret: str = Field(..., description="Your registered client secret with the upstream provider")
|
|
466
|
+
upstream_revocation_endpoint: str | None = Field(None, description="Optional upstream token revocation endpoint")
|
|
467
|
+
|
|
468
|
+
# This proxy server configuration
|
|
469
|
+
base_url: str = Field(..., description="Public URL of this OAuth proxy server")
|
|
470
|
+
redirect_path: str = Field("/oauth/callback", description="OAuth callback path (must match provider registration)")
|
|
471
|
+
|
|
472
|
+
# Scopes and token verification
|
|
473
|
+
scopes_supported: list[str] = Field(default_factory=list, description="Scopes supported by this proxy")
|
|
474
|
+
token_verifier_config: JWTAuthConfig | StaticTokenConfig = Field(
|
|
475
|
+
..., description="Token verifier configuration for validating upstream tokens"
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
# Environment variable names for runtime configuration
|
|
479
|
+
upstream_authorization_endpoint_env_var: str | None = Field(
|
|
480
|
+
None, description="Environment variable name for upstream authorization endpoint"
|
|
481
|
+
)
|
|
482
|
+
upstream_token_endpoint_env_var: str | None = Field(
|
|
483
|
+
None, description="Environment variable name for upstream token endpoint"
|
|
484
|
+
)
|
|
485
|
+
upstream_client_id_env_var: str | None = Field(None, description="Environment variable name for upstream client ID")
|
|
486
|
+
upstream_client_secret_env_var: str | None = Field(
|
|
487
|
+
None, description="Environment variable name for upstream client secret"
|
|
488
|
+
)
|
|
489
|
+
upstream_revocation_endpoint_env_var: str | None = Field(
|
|
490
|
+
None, description="Environment variable name for upstream revocation endpoint"
|
|
491
|
+
)
|
|
492
|
+
base_url_env_var: str | None = Field(None, description="Environment variable name for base URL")
|
|
493
|
+
|
|
494
|
+
@field_validator("upstream_authorization_endpoint", "upstream_token_endpoint", "base_url")
|
|
495
|
+
@classmethod
|
|
496
|
+
def validate_required_urls(cls, v: str) -> str:
|
|
497
|
+
"""Validate required URLs are properly formatted."""
|
|
498
|
+
if not v or not v.strip():
|
|
499
|
+
raise ValueError("URL cannot be empty")
|
|
500
|
+
|
|
501
|
+
url = v.strip()
|
|
502
|
+
try:
|
|
503
|
+
from urllib.parse import urlparse
|
|
504
|
+
|
|
505
|
+
parsed = urlparse(url)
|
|
506
|
+
if not parsed.scheme or not parsed.netloc:
|
|
507
|
+
raise ValueError(f"Invalid URL format: '{url}' - must include scheme and netloc")
|
|
508
|
+
if parsed.scheme not in ("http", "https"):
|
|
509
|
+
raise ValueError(f"URL must use http or https scheme: '{url}'")
|
|
510
|
+
except Exception as e:
|
|
511
|
+
if isinstance(e, ValueError):
|
|
512
|
+
raise
|
|
513
|
+
raise ValueError(f"Invalid URL '{url}': {e}") from e
|
|
514
|
+
|
|
515
|
+
return url
|
|
516
|
+
|
|
517
|
+
@field_validator("upstream_revocation_endpoint")
|
|
518
|
+
@classmethod
|
|
519
|
+
def validate_optional_url(cls, v: str | None) -> str | None:
|
|
520
|
+
"""Validate optional URLs are properly formatted."""
|
|
521
|
+
if not v:
|
|
522
|
+
return v
|
|
523
|
+
|
|
524
|
+
url = v.strip()
|
|
525
|
+
if not url:
|
|
526
|
+
return None
|
|
527
|
+
|
|
528
|
+
try:
|
|
529
|
+
from urllib.parse import urlparse
|
|
530
|
+
|
|
531
|
+
parsed = urlparse(url)
|
|
532
|
+
if not parsed.scheme or not parsed.netloc:
|
|
533
|
+
raise ValueError(f"Invalid URL format: '{url}' - must include scheme and netloc")
|
|
534
|
+
if parsed.scheme not in ("http", "https"):
|
|
535
|
+
raise ValueError(f"URL must use http or https scheme: '{url}'")
|
|
536
|
+
except Exception as e:
|
|
537
|
+
if isinstance(e, ValueError):
|
|
538
|
+
raise
|
|
539
|
+
raise ValueError(f"Invalid URL '{url}': {e}") from e
|
|
540
|
+
|
|
541
|
+
return url
|
|
542
|
+
|
|
543
|
+
@field_validator("scopes_supported")
|
|
544
|
+
@classmethod
|
|
545
|
+
def validate_scopes_supported(cls, v: list[str]) -> list[str]:
|
|
546
|
+
"""Validate scopes_supported format and security."""
|
|
547
|
+
if not v:
|
|
548
|
+
return v
|
|
549
|
+
|
|
550
|
+
cleaned_scopes = []
|
|
551
|
+
for scope in v:
|
|
552
|
+
scope = scope.strip()
|
|
553
|
+
if not scope:
|
|
554
|
+
raise ValueError("Scopes cannot be empty or whitespace-only")
|
|
555
|
+
|
|
556
|
+
# OAuth 2.0 scope format validation (RFC 6749)
|
|
557
|
+
if not all(32 < ord(c) < 127 and c not in ' "\\' for c in scope):
|
|
558
|
+
raise ValueError(
|
|
559
|
+
f"Invalid scope format: '{scope}' - must be ASCII printable without spaces, quotes, or backslashes"
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
# Reasonable length limit to prevent abuse
|
|
563
|
+
if len(scope) > 128:
|
|
564
|
+
raise ValueError(f"Scope too long: '{scope}' - maximum 128 characters")
|
|
565
|
+
|
|
566
|
+
cleaned_scopes.append(scope)
|
|
567
|
+
|
|
568
|
+
return cleaned_scopes
|
|
569
|
+
|
|
570
|
+
@model_validator(mode="after")
|
|
571
|
+
def validate_oauth_proxy_config(self) -> "OAuthProxyConfig":
|
|
572
|
+
"""Validate OAuth proxy configuration consistency."""
|
|
573
|
+
# Validate token verifier config is compatible
|
|
574
|
+
if not isinstance(self.token_verifier_config, JWTAuthConfig | StaticTokenConfig):
|
|
575
|
+
raise ValueError(
|
|
576
|
+
f"token_verifier_config must be JWTAuthConfig or StaticTokenConfig, got {type(self.token_verifier_config).__name__}"
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
# Warn about HTTPS requirements in production
|
|
580
|
+
is_production = (
|
|
581
|
+
os.environ.get("GOLF_ENV", "").lower() in ("prod", "production")
|
|
582
|
+
or os.environ.get("NODE_ENV", "").lower() == "production"
|
|
583
|
+
or os.environ.get("ENVIRONMENT", "").lower() in ("prod", "production")
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
if is_production:
|
|
587
|
+
from urllib.parse import urlparse
|
|
588
|
+
|
|
589
|
+
urls_to_check = [
|
|
590
|
+
("base_url", self.base_url),
|
|
591
|
+
("upstream_authorization_endpoint", self.upstream_authorization_endpoint),
|
|
592
|
+
("upstream_token_endpoint", self.upstream_token_endpoint),
|
|
593
|
+
]
|
|
594
|
+
|
|
595
|
+
if self.upstream_revocation_endpoint:
|
|
596
|
+
urls_to_check.append(("upstream_revocation_endpoint", self.upstream_revocation_endpoint))
|
|
597
|
+
|
|
598
|
+
for field_name, url in urls_to_check:
|
|
599
|
+
parsed = urlparse(url)
|
|
600
|
+
if parsed.scheme == "http":
|
|
601
|
+
import warnings
|
|
602
|
+
|
|
603
|
+
warnings.warn(
|
|
604
|
+
f"OAuth proxy {field_name} '{url}' uses HTTP in production environment. "
|
|
605
|
+
"HTTPS is strongly recommended for OAuth endpoints to prevent token interception.",
|
|
606
|
+
UserWarning,
|
|
607
|
+
stacklevel=2,
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
return self
|
|
611
|
+
|
|
612
|
+
|
|
445
613
|
# Union type for all auth configurations
|
|
446
|
-
AuthConfig = JWTAuthConfig | StaticTokenConfig | OAuthServerConfig | RemoteAuthConfig
|
|
614
|
+
AuthConfig = JWTAuthConfig | StaticTokenConfig | OAuthServerConfig | RemoteAuthConfig | OAuthProxyConfig
|
|
@@ -64,7 +64,7 @@ def generate_auth_code(
|
|
|
64
64
|
f"auth_config = {auth_config_repr}",
|
|
65
65
|
"try:",
|
|
66
66
|
" auth_provider = create_auth_provider(auth_config)",
|
|
67
|
-
"
|
|
67
|
+
" # Authentication configured with {auth_config.provider_type} provider",
|
|
68
68
|
"except Exception as e:",
|
|
69
69
|
" print(f'Authentication setup failed: {e}', file=sys.stderr)",
|
|
70
70
|
" auth_provider = None",
|
|
@@ -203,7 +203,7 @@ if auth_provider and hasattr(auth_provider, 'get_routes'):
|
|
|
203
203
|
# Add routes to FastMCP's additional HTTP routes list
|
|
204
204
|
try:
|
|
205
205
|
mcp._additional_http_routes.extend(auth_routes)
|
|
206
|
-
|
|
206
|
+
# Added {len(auth_routes)} OAuth metadata routes
|
|
207
207
|
except Exception as e:
|
|
208
208
|
print(f"Warning: Failed to add OAuth routes: {e}")
|
|
209
209
|
"""
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
setup.py
|
|
6
|
+
.docs/docs.md
|
|
7
|
+
.docs/fastmcp-diff.md
|
|
8
|
+
.docs/mcp.md
|
|
9
|
+
src/golf/__init__.py
|
|
10
|
+
src/golf/_endpoints.py.in
|
|
11
|
+
src/golf/_endpoints_fallback.py
|
|
12
|
+
src/golf/auth/__init__.py
|
|
13
|
+
src/golf/auth/api_key.py
|
|
14
|
+
src/golf/auth/factory.py
|
|
15
|
+
src/golf/auth/helpers.py
|
|
16
|
+
src/golf/auth/providers.py
|
|
17
|
+
src/golf/auth/registry.py
|
|
18
|
+
src/golf/cli/__init__.py
|
|
19
|
+
src/golf/cli/branding.py
|
|
20
|
+
src/golf/cli/main.py
|
|
21
|
+
src/golf/commands/__init__.py
|
|
22
|
+
src/golf/commands/build.py
|
|
23
|
+
src/golf/commands/init.py
|
|
24
|
+
src/golf/commands/run.py
|
|
25
|
+
src/golf/core/__init__.py
|
|
26
|
+
src/golf/core/builder.py
|
|
27
|
+
src/golf/core/builder_auth.py
|
|
28
|
+
src/golf/core/builder_metrics.py
|
|
29
|
+
src/golf/core/builder_telemetry.py
|
|
30
|
+
src/golf/core/config.py
|
|
31
|
+
src/golf/core/parser.py
|
|
32
|
+
src/golf/core/platform.py
|
|
33
|
+
src/golf/core/telemetry.py
|
|
34
|
+
src/golf/core/transformer.py
|
|
35
|
+
src/golf/examples/__init__.py
|
|
36
|
+
src/golf/examples/basic/.env.example
|
|
37
|
+
src/golf/examples/basic/README.md
|
|
38
|
+
src/golf/examples/basic/auth.py
|
|
39
|
+
src/golf/examples/basic/golf.json
|
|
40
|
+
src/golf/examples/basic/prompts/welcome.py
|
|
41
|
+
src/golf/examples/basic/resources/current_time.py
|
|
42
|
+
src/golf/examples/basic/resources/info.py
|
|
43
|
+
src/golf/examples/basic/resources/weather/city.py
|
|
44
|
+
src/golf/examples/basic/resources/weather/common.py
|
|
45
|
+
src/golf/examples/basic/resources/weather/current.py
|
|
46
|
+
src/golf/examples/basic/resources/weather/forecast.py
|
|
47
|
+
src/golf/examples/basic/tools/calculator.py
|
|
48
|
+
src/golf/examples/basic/tools/say/hello.py
|
|
49
|
+
src/golf/metrics/__init__.py
|
|
50
|
+
src/golf/metrics/collector.py
|
|
51
|
+
src/golf/metrics/registry.py
|
|
52
|
+
src/golf/telemetry/__init__.py
|
|
53
|
+
src/golf/telemetry/instrumentation.py
|
|
54
|
+
src/golf/utilities/__init__.py
|
|
55
|
+
src/golf/utilities/context.py
|
|
56
|
+
src/golf/utilities/elicitation.py
|
|
57
|
+
src/golf/utilities/sampling.py
|
|
58
|
+
src/golf_mcp.egg-info/PKG-INFO
|
|
59
|
+
src/golf_mcp.egg-info/SOURCES.txt
|
|
60
|
+
src/golf_mcp.egg-info/dependency_links.txt
|
|
61
|
+
src/golf_mcp.egg-info/entry_points.txt
|
|
62
|
+
src/golf_mcp.egg-info/requires.txt
|
|
63
|
+
src/golf_mcp.egg-info/top_level.txt
|
|
Binary file
|