golf-mcp 0.2.16__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.
- golf/__init__.py +1 -0
- golf/auth/__init__.py +277 -0
- golf/auth/api_key.py +73 -0
- golf/auth/factory.py +360 -0
- golf/auth/helpers.py +175 -0
- golf/auth/providers.py +586 -0
- golf/auth/registry.py +256 -0
- golf/cli/__init__.py +1 -0
- golf/cli/branding.py +191 -0
- golf/cli/main.py +377 -0
- golf/commands/__init__.py +5 -0
- golf/commands/build.py +81 -0
- golf/commands/init.py +290 -0
- golf/commands/run.py +137 -0
- golf/core/__init__.py +1 -0
- golf/core/builder.py +1884 -0
- golf/core/builder_auth.py +209 -0
- golf/core/builder_metrics.py +221 -0
- golf/core/builder_telemetry.py +99 -0
- golf/core/config.py +199 -0
- golf/core/parser.py +1085 -0
- golf/core/telemetry.py +492 -0
- golf/core/transformer.py +231 -0
- golf/examples/__init__.py +0 -0
- golf/examples/basic/.env.example +4 -0
- golf/examples/basic/README.md +133 -0
- golf/examples/basic/auth.py +76 -0
- golf/examples/basic/golf.json +5 -0
- golf/examples/basic/prompts/welcome.py +27 -0
- golf/examples/basic/resources/current_time.py +34 -0
- golf/examples/basic/resources/info.py +28 -0
- golf/examples/basic/resources/weather/city.py +46 -0
- golf/examples/basic/resources/weather/client.py +48 -0
- golf/examples/basic/resources/weather/current.py +36 -0
- golf/examples/basic/resources/weather/forecast.py +36 -0
- golf/examples/basic/tools/calculator.py +94 -0
- golf/examples/basic/tools/say/hello.py +65 -0
- golf/metrics/__init__.py +10 -0
- golf/metrics/collector.py +320 -0
- golf/metrics/registry.py +12 -0
- golf/telemetry/__init__.py +23 -0
- golf/telemetry/instrumentation.py +1402 -0
- 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.2.16.dist-info/METADATA +262 -0
- golf_mcp-0.2.16.dist-info/RECORD +52 -0
- golf_mcp-0.2.16.dist-info/WHEEL +5 -0
- golf_mcp-0.2.16.dist-info/entry_points.txt +2 -0
- golf_mcp-0.2.16.dist-info/licenses/LICENSE +201 -0
- golf_mcp-0.2.16.dist-info/top_level.txt +1 -0
golf/core/config.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""Configuration management for GolfMCP."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field, field_validator
|
|
7
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AuthConfig(BaseModel):
|
|
14
|
+
"""Authentication configuration."""
|
|
15
|
+
|
|
16
|
+
provider: str = Field(..., description="Authentication provider (e.g., 'jwks', 'google', 'github')")
|
|
17
|
+
scopes: list[str] = Field(default_factory=list, description="Required OAuth scopes")
|
|
18
|
+
client_id_env: str | None = Field(None, description="Environment variable name for client ID")
|
|
19
|
+
client_secret_env: str | None = Field(None, description="Environment variable name for client secret")
|
|
20
|
+
redirect_uri: str | None = Field(None, description="OAuth redirect URI (defaults to localhost callback)")
|
|
21
|
+
|
|
22
|
+
@field_validator("provider")
|
|
23
|
+
@classmethod
|
|
24
|
+
def validate_provider(cls, value: str) -> str:
|
|
25
|
+
"""Validate the provider value."""
|
|
26
|
+
valid_providers = {"jwks", "google", "github", "custom"}
|
|
27
|
+
if value not in valid_providers and not value.startswith("custom:"):
|
|
28
|
+
raise ValueError(f"Invalid provider '{value}'. Must be one of {valid_providers} or start with 'custom:'")
|
|
29
|
+
return value
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class DeployConfig(BaseModel):
|
|
33
|
+
"""Deployment configuration."""
|
|
34
|
+
|
|
35
|
+
default: str = Field("vercel", description="Default deployment target")
|
|
36
|
+
options: dict[str, Any] = Field(default_factory=dict, description="Target-specific options")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Settings(BaseSettings):
|
|
40
|
+
"""GolfMCP application settings."""
|
|
41
|
+
|
|
42
|
+
model_config = SettingsConfigDict(
|
|
43
|
+
env_prefix="GOLF_",
|
|
44
|
+
env_file=".env",
|
|
45
|
+
env_file_encoding="utf-8",
|
|
46
|
+
extra="ignore",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Project metadata
|
|
50
|
+
name: str = Field("GolfMCP Project", description="FastMCP instance name")
|
|
51
|
+
description: str | None = Field(None, description="Project description")
|
|
52
|
+
|
|
53
|
+
# Build settings
|
|
54
|
+
output_dir: str = Field("dist", description="Build artifact folder")
|
|
55
|
+
|
|
56
|
+
# Server settings
|
|
57
|
+
host: str = Field("localhost", description="Server host")
|
|
58
|
+
port: int = Field(3000, description="Server port")
|
|
59
|
+
transport: str = Field(
|
|
60
|
+
"streamable-http",
|
|
61
|
+
description="Transport protocol (streamable-http, sse, stdio)",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Auth settings
|
|
65
|
+
auth: str | AuthConfig | None = Field(None, description="Authentication configuration or URI")
|
|
66
|
+
|
|
67
|
+
# Deploy settings
|
|
68
|
+
deploy: DeployConfig = Field(default_factory=DeployConfig, description="Deployment configuration")
|
|
69
|
+
|
|
70
|
+
# Feature flags
|
|
71
|
+
telemetry: bool = Field(True, description="Enable anonymous telemetry")
|
|
72
|
+
|
|
73
|
+
# Project paths
|
|
74
|
+
tools_dir: str = Field("tools", description="Directory containing tools")
|
|
75
|
+
resources_dir: str = Field("resources", description="Directory containing resources")
|
|
76
|
+
prompts_dir: str = Field("prompts", description="Directory containing prompts")
|
|
77
|
+
|
|
78
|
+
# OpenTelemetry config
|
|
79
|
+
opentelemetry_enabled: bool = Field(False, description="Enable OpenTelemetry tracing")
|
|
80
|
+
opentelemetry_default_exporter: str = Field("console", description="Default OpenTelemetry exporter type")
|
|
81
|
+
detailed_tracing: bool = Field(
|
|
82
|
+
False, description="Enable detailed tracing with input/output capture (may contain sensitive data)"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Health check configuration
|
|
86
|
+
health_check_enabled: bool = Field(False, description="Enable health check endpoint (deprecated - use health.py)")
|
|
87
|
+
health_check_path: str = Field("/health", description="Health check endpoint path")
|
|
88
|
+
health_check_response: str = Field("OK", description="Health check response text (deprecated - use health.py)")
|
|
89
|
+
|
|
90
|
+
# HTTP session behaviour
|
|
91
|
+
stateless_http: bool = Field(
|
|
92
|
+
False,
|
|
93
|
+
description="Make Streamable-HTTP transport stateless (new session per request)",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Metrics configuration
|
|
97
|
+
metrics_enabled: bool = Field(False, description="Enable Prometheus metrics endpoint")
|
|
98
|
+
metrics_path: str = Field("/metrics", description="Metrics endpoint path")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def find_config_path(start_path: Path | None = None) -> Path | None:
|
|
102
|
+
"""Find the golf config file by searching upwards from the given path.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
start_path: Path to start searching from (defaults to current directory)
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Path to the config file if found, None otherwise
|
|
109
|
+
"""
|
|
110
|
+
if start_path is None:
|
|
111
|
+
start_path = Path.cwd()
|
|
112
|
+
|
|
113
|
+
current = start_path.absolute()
|
|
114
|
+
|
|
115
|
+
# Don't search above the home directory
|
|
116
|
+
home = Path.home().absolute()
|
|
117
|
+
|
|
118
|
+
while current != current.parent and current != home:
|
|
119
|
+
# Check for JSON config first (preferred)
|
|
120
|
+
json_config = current / "golf.json"
|
|
121
|
+
if json_config.exists():
|
|
122
|
+
return json_config
|
|
123
|
+
|
|
124
|
+
# Fall back to TOML config
|
|
125
|
+
toml_config = current / "golf.toml"
|
|
126
|
+
if toml_config.exists():
|
|
127
|
+
return toml_config
|
|
128
|
+
|
|
129
|
+
current = current.parent
|
|
130
|
+
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def find_project_root(
|
|
135
|
+
start_path: Path | None = None,
|
|
136
|
+
) -> tuple[Path | None, Path | None]:
|
|
137
|
+
"""Find a GolfMCP project root by searching for a config file.
|
|
138
|
+
|
|
139
|
+
This is the central project discovery function that should be used by all commands.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
start_path: Path to start searching from (defaults to current directory)
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Tuple of (project_root, config_path) if a project is found, or
|
|
146
|
+
(None, None) if not
|
|
147
|
+
"""
|
|
148
|
+
config_path = find_config_path(start_path)
|
|
149
|
+
if config_path:
|
|
150
|
+
return config_path.parent, config_path
|
|
151
|
+
return None, None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def load_settings(project_path: str | Path) -> Settings:
|
|
155
|
+
"""Load settings from a project directory.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
project_path: Path to the project directory
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Settings object with values loaded from config files
|
|
162
|
+
"""
|
|
163
|
+
# Convert to Path if needed
|
|
164
|
+
if isinstance(project_path, str):
|
|
165
|
+
project_path = Path(project_path)
|
|
166
|
+
|
|
167
|
+
# Create default settings
|
|
168
|
+
settings = Settings()
|
|
169
|
+
|
|
170
|
+
# Check for .env file
|
|
171
|
+
env_file = project_path / ".env"
|
|
172
|
+
if env_file.exists():
|
|
173
|
+
settings = Settings(_env_file=env_file)
|
|
174
|
+
|
|
175
|
+
# Try to load JSON config file first
|
|
176
|
+
json_config_path = project_path / "golf.json"
|
|
177
|
+
if json_config_path.exists():
|
|
178
|
+
return _load_json_settings(json_config_path, settings)
|
|
179
|
+
|
|
180
|
+
return settings
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _load_json_settings(path: Path, settings: Settings) -> Settings:
|
|
184
|
+
"""Load settings from a JSON file."""
|
|
185
|
+
try:
|
|
186
|
+
import json
|
|
187
|
+
|
|
188
|
+
with open(path) as f:
|
|
189
|
+
config_data = json.load(f)
|
|
190
|
+
|
|
191
|
+
# Update settings from config data
|
|
192
|
+
for key, value in config_data.items():
|
|
193
|
+
if hasattr(settings, key):
|
|
194
|
+
setattr(settings, key, value)
|
|
195
|
+
|
|
196
|
+
return settings
|
|
197
|
+
except Exception as e:
|
|
198
|
+
console.print(f"[bold red]Error loading JSON config from {path}: {e}[/bold red]")
|
|
199
|
+
return settings
|