golf-mcp 0.2.11__py3-none-any.whl → 0.2.13__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 CHANGED
@@ -1,9 +1 @@
1
- __version__ = "0.2.11"
2
-
3
- # Import endpoints with fallback for dev mode
4
- try:
5
- # In built wheels, this exists (generated from _endpoints.py.in)
6
- from . import _endpoints
7
- except ImportError:
8
- # In editable/dev installs, fall back to env-based values
9
- from . import _endpoints_fallback as _endpoints
1
+ __version__ = "0.2.13"
golf/auth/__init__.py CHANGED
@@ -32,11 +32,9 @@ from .registry import (
32
32
  # Re-export for backward compatibility
33
33
  from .api_key import configure_api_key, get_api_key_config, is_api_key_configured
34
34
  from .helpers import (
35
- debug_api_key_context,
36
35
  extract_token_from_header,
37
36
  get_api_key,
38
37
  get_auth_token,
39
- get_provider_token,
40
38
  set_api_key,
41
39
  )
42
40
 
@@ -70,11 +68,9 @@ __all__ = [
70
68
  "get_api_key_config",
71
69
  "is_api_key_configured",
72
70
  # Helper functions
73
- "debug_api_key_context",
74
71
  "extract_token_from_header",
75
72
  "get_api_key",
76
73
  "get_auth_token",
77
- "get_provider_token",
78
74
  "set_api_key",
79
75
  ]
80
76
 
golf/auth/helpers.py CHANGED
@@ -1,26 +1,12 @@
1
1
  """Helper functions for working with authentication in MCP context."""
2
2
 
3
3
  from contextvars import ContextVar
4
- from typing import Any
5
4
 
6
- from starlette.requests import Request
7
5
 
8
6
  # Context variable to store the current request's API key
9
7
  _current_api_key: ContextVar[str | None] = ContextVar("current_api_key", default=None)
10
8
 
11
9
 
12
- def get_provider_token() -> str | None:
13
- """
14
- Get a provider token (legacy function - no longer supported in Golf 0.2.x).
15
-
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.
18
- """
19
- # Legacy OAuth provider support removed in Golf 0.2.x
20
- # Use FastMCP 2.11+ auth providers instead
21
- return None
22
-
23
-
24
10
  def extract_token_from_header(auth_header: str) -> str | None:
25
11
  """Extract bearer token from Authorization header.
26
12
 
@@ -123,25 +109,6 @@ def get_api_key() -> str | None:
123
109
  return None
124
110
 
125
111
 
126
- def get_api_key_from_request(request: Request) -> str | None:
127
- """Get the API key from a specific request object.
128
-
129
- This is useful when you have direct access to the request object.
130
-
131
- Args:
132
- request: The Starlette Request object
133
-
134
- Returns:
135
- The API key if available, None otherwise
136
- """
137
- # Check request state first (set by our middleware)
138
- if hasattr(request, "state") and hasattr(request.state, "api_key"):
139
- return request.state.api_key
140
-
141
- # Fall back to context variable
142
- return _current_api_key.get()
143
-
144
-
145
112
  def get_auth_token() -> str | None:
146
113
  """Get the authorization token from the current request context.
147
114
 
@@ -206,52 +173,3 @@ def get_auth_token() -> str | None:
206
173
  pass
207
174
 
208
175
  return None
209
-
210
-
211
- def debug_api_key_context() -> dict[str, Any]:
212
- """Debug function to inspect API key context.
213
-
214
- Returns a dictionary with debugging information about the current
215
- API key context. Useful for troubleshooting authentication issues.
216
-
217
- Returns:
218
- Dictionary with debug information
219
- """
220
- import asyncio
221
- import os
222
- import sys
223
-
224
- debug_info = {
225
- "context_var_value": _current_api_key.get(),
226
- "has_async_task": False,
227
- "task_id": None,
228
- "main_module_has_storage": False,
229
- "main_module_has_context": False,
230
- "request_id_from_context": None,
231
- "env_vars": {
232
- "API_KEY": bool(os.environ.get("API_KEY")),
233
- "GOLF_API_KEY_DEBUG": os.environ.get("GOLF_API_KEY_DEBUG", "false"),
234
- },
235
- }
236
-
237
- try:
238
- task = asyncio.current_task()
239
- if task:
240
- debug_info["has_async_task"] = True
241
- debug_info["task_id"] = id(task)
242
- except Exception:
243
- pass
244
-
245
- try:
246
- main_module = sys.modules.get("__main__")
247
- if main_module:
248
- debug_info["main_module_has_storage"] = hasattr(main_module, "api_key_storage")
249
- debug_info["main_module_has_context"] = hasattr(main_module, "request_id_context")
250
-
251
- if hasattr(main_module, "request_id_context"):
252
- request_id_context = main_module.request_id_context
253
- debug_info["request_id_from_context"] = request_id_context.get()
254
- except Exception:
255
- pass
256
-
257
- return debug_info
golf/cli/branding.py CHANGED
@@ -33,7 +33,6 @@ STATUS_ICONS = {
33
33
  "building": "🔨",
34
34
  "generating": "⚙️",
35
35
  "packaging": "📦",
36
- "platform": "⛳",
37
36
  "server": "🚀",
38
37
  "loading": "⭕",
39
38
  }
@@ -163,7 +162,7 @@ def get_status_text(status: str, message: str, style: str = "") -> Text:
163
162
  elif status == "warning":
164
163
  text.append("⚡ ", style=f"bold {GOLF_ORANGE}")
165
164
  text.append(f"{icon} {message}", style=f"bold {GOLF_ORANGE}")
166
- elif status in ["building", "generating", "packaging", "platform"]:
165
+ elif status in ["building", "generating", "packaging"]:
167
166
  text.append("🔥 ", style=f"bold {GOLF_ORANGE}")
168
167
  text.append(f"{icon} {message}", style=f"bold {GOLF_BLUE}")
169
168
  else:
golf/cli/main.py CHANGED
@@ -167,11 +167,9 @@ def build_prod(
167
167
  ) -> None:
168
168
  """Build a production version for deployment.
169
169
 
170
- Golf credentials (GOLF_*) are always loaded from .env for build operations
171
- (platform registration, resource updates). App environment variables are
172
- NOT copied for security - provide them in your deployment environment.
173
-
174
- Your production deployment must include GOLF_* vars for runtime telemetry.
170
+ Environment variables from .env are loaded for build operations.
171
+ App environment variables are NOT copied for security - provide them
172
+ in your deployment environment.
175
173
  """
176
174
  # Find project root directory
177
175
  project_root, config_path = find_project_root()
golf/commands/run.py CHANGED
@@ -5,7 +5,7 @@ import subprocess
5
5
  import sys
6
6
  from pathlib import Path
7
7
 
8
- from rich.console import Console
8
+ from rich.console import Console, Group
9
9
  from rich.panel import Panel
10
10
  from rich.align import Align
11
11
  from rich.text import Text
@@ -53,23 +53,35 @@ def run_server(
53
53
  server_host = host or settings.host or "localhost"
54
54
  server_port = port or settings.port or 3000
55
55
 
56
- server_content = Text()
57
- server_content.append("🚀 ", style=f"bold {GOLF_ORANGE}")
58
- server_content.append(f"{STATUS_ICONS['server']} Server starting on ", style=f"bold {GOLF_BLUE}")
59
- server_content.append(f"http://{server_host}:{server_port}", style=f"bold {GOLF_GREEN}")
60
- server_content.append(" 🚀", style=f"bold {GOLF_ORANGE}")
61
- server_content.append("\n")
56
+ # Create server URL line
57
+ server_line = Text()
58
+ server_line.append("🚀 ", style=f"bold {GOLF_ORANGE}")
59
+ server_line.append(f"{STATUS_ICONS['server']} Server starting on ", style=f"bold {GOLF_BLUE}")
60
+ server_line.append(f"http://{server_host}:{server_port}", style=f"bold {GOLF_GREEN}")
61
+
62
+ # Create content with proper alignment
63
+ content_lines = [
64
+ "", # Empty line at top
65
+ Align.center(server_line),
66
+ ]
62
67
 
63
68
  # Add telemetry status indicator
64
69
  if settings.opentelemetry_enabled:
65
- server_content.append("📊 Golf telemetry enabled", style=f"dim {GOLF_BLUE}")
66
- server_content.append("\n")
67
-
68
- server_content.append("⚡ Press Ctrl+C to stop ⚡", style=f"dim {GOLF_ORANGE}")
70
+ telemetry_line = Text("📊 OpenTelemetry enabled", style=f"dim {GOLF_BLUE}")
71
+ content_lines.append(Align.center(telemetry_line))
72
+
73
+ # Add empty line and stop instruction
74
+ content_lines.extend(
75
+ [
76
+ "", # Empty line before stop instruction
77
+ Align.center(Text("⚡ Press Ctrl+C to stop ⚡", style=f"dim {GOLF_ORANGE}")),
78
+ "", # Empty line at bottom
79
+ ]
80
+ )
69
81
 
70
82
  console.print(
71
83
  Panel(
72
- Align.center(server_content),
84
+ Group(*content_lines),
73
85
  border_style=GOLF_BLUE,
74
86
  padding=(1, 2),
75
87
  title="[bold]🌐 SERVER READY 🌐[/bold]",
golf/core/builder.py CHANGED
@@ -16,7 +16,7 @@ from golf.core.builder_auth import generate_auth_code, generate_auth_routes
16
16
  from golf.core.builder_telemetry import (
17
17
  generate_telemetry_imports,
18
18
  )
19
- from golf.cli.branding import create_build_header, get_status_text, STATUS_ICONS, GOLF_BLUE
19
+ from golf.cli.branding import create_build_header, get_status_text
20
20
  from golf.core.config import Settings
21
21
  from golf.core.parser import (
22
22
  ComponentType,
@@ -383,6 +383,22 @@ class CodeGenerator:
383
383
  console.print()
384
384
  console.print(get_status_text("success", f"Build completed successfully in {output_dir_display}"))
385
385
 
386
+ def _generate_root_file_imports(self) -> list[str]:
387
+ """Generate import statements for automatically discovered root files."""
388
+ root_file_imports = []
389
+ discovered_files = discover_root_files(self.project_path)
390
+
391
+ if discovered_files:
392
+ root_file_imports.append("# Import root-level Python files")
393
+
394
+ for filename in sorted(discovered_files.keys()):
395
+ module_name = Path(filename).stem # env.py -> env
396
+ root_file_imports.append(f"import {module_name}")
397
+
398
+ root_file_imports.append("") # Blank line
399
+
400
+ return root_file_imports
401
+
386
402
  def _create_directory_structure(self) -> None:
387
403
  """Create the output directory structure"""
388
404
  # Create main directories
@@ -810,6 +826,11 @@ class CodeGenerator:
810
826
  "",
811
827
  ]
812
828
 
829
+ # Add imports for root files
830
+ root_file_imports = self._generate_root_file_imports()
831
+ if root_file_imports:
832
+ imports.extend(root_file_imports)
833
+
813
834
  # Add auth imports if auth is configured
814
835
  if auth_components.get("has_auth"):
815
836
  imports.extend(auth_components["imports"])
@@ -1401,21 +1422,13 @@ def build_project(
1401
1422
  build_env: Build environment ('dev' or 'prod')
1402
1423
  copy_env: Whether to copy environment variables to the built app
1403
1424
  """
1404
- # Load Golf credentials from .env for build operations (platform registration, etc.)
1405
- # This happens regardless of copy_env setting to ensure build process works
1425
+ # Load environment variables from .env for build operations
1406
1426
  from dotenv import load_dotenv
1407
1427
 
1408
1428
  project_env_file = project_path / ".env"
1409
1429
  if project_env_file.exists():
1410
- # Load GOLF_* variables for build process
1411
1430
  load_dotenv(project_env_file, override=False)
1412
1431
 
1413
- # Only log if we actually found the specific Golf platform credentials
1414
- has_api_key = "GOLF_API_KEY" in os.environ
1415
- has_server_id = "GOLF_SERVER_ID" in os.environ
1416
- if has_api_key and has_server_id:
1417
- console.print("[dim]Loaded Golf credentials for build operations[/dim]")
1418
-
1419
1432
  # Execute auth.py if it exists (for authentication configuration)
1420
1433
  # Also support legacy pre_build.py for backward compatibility
1421
1434
  auth_path = project_path / "auth.py"
@@ -1583,32 +1596,16 @@ def build_project(
1583
1596
  shutil.copy2(health_path, output_dir)
1584
1597
  console.print(get_status_text("success", "Health script copied to build directory"))
1585
1598
 
1586
- # Platform registration (only for prod builds)
1587
- if build_env == "prod":
1588
- console.print()
1589
- status_msg = f"[{GOLF_BLUE}]{STATUS_ICONS['platform']} Registering with Golf platform and updating resources...[/{GOLF_BLUE}]"
1590
- with console.status(status_msg):
1591
- import asyncio
1592
-
1593
- try:
1594
- from golf.core.platform import register_project_with_platform
1595
-
1596
- success = asyncio.run(
1597
- register_project_with_platform(
1598
- project_path=project_path,
1599
- settings=settings,
1600
- components=generator.components,
1601
- )
1602
- )
1599
+ # Copy any additional Python files from project root
1600
+ discovered_root_files = discover_root_files(project_path)
1603
1601
 
1604
- if success:
1605
- console.print(get_status_text("success", "Platform registration completed"))
1606
- # If success is False, the platform module already printed appropriate warnings
1607
- except ImportError:
1608
- console.print(get_status_text("warning", "Platform registration module not available"))
1609
- except Exception as e:
1610
- console.print(get_status_text("warning", f"Platform registration failed: {e}"))
1611
- console.print("[dim]Tip: Ensure GOLF_API_KEY and GOLF_SERVER_ID are available in your .env file[/dim]")
1602
+ for filename, file_path in discovered_root_files.items():
1603
+ dest_path = output_dir / filename
1604
+ try:
1605
+ shutil.copy2(file_path, dest_path)
1606
+ console.print(get_status_text("success", f"Root file {filename} copied to build directory"))
1607
+ except (OSError, shutil.Error) as e:
1608
+ console.print(f"[red]Error copying {filename}: {e}[/red]")
1612
1609
 
1613
1610
  # Create a simple README
1614
1611
  readme_content = f"""# {settings.name}
@@ -1639,7 +1636,7 @@ This is a standalone FastMCP server generated by GolfMCP.
1639
1636
 
1640
1637
  # Legacy ProviderConfig removed in Golf 0.2.x - use modern auth configurations
1641
1638
  # Legacy OAuth imports removed in Golf 0.2.x - use FastMCP 2.11+ auth providers
1642
- from golf.auth.helpers import get_provider_token, extract_token_from_header, get_api_key, set_api_key
1639
+ from golf.auth.helpers import extract_token_from_header, get_api_key, set_api_key
1643
1640
  from golf.auth.api_key import configure_api_key, get_api_key_config
1644
1641
  from golf.auth.factory import create_auth_provider
1645
1642
  from golf.auth.providers import RemoteAuthConfig, JWTAuthConfig, StaticTokenConfig, OAuthServerConfig
@@ -1709,6 +1706,86 @@ from golf.auth.providers import RemoteAuthConfig, JWTAuthConfig, StaticTokenConf
1709
1706
  )
1710
1707
 
1711
1708
 
1709
+ def discover_root_files(project_path: Path) -> dict[str, Path]:
1710
+ """Automatically discover all Python files in the project root directory.
1711
+
1712
+ This function finds all .py files in the project root, excluding:
1713
+ - Special Golf files (startup.py, health.py, readiness.py, auth.py, server.py)
1714
+ - Component directories (tools/, resources/, prompts/)
1715
+ - Hidden files and common exclusions (__pycache__, .git, etc.)
1716
+
1717
+ Args:
1718
+ project_path: Path to the project root directory
1719
+
1720
+ Returns:
1721
+ Dictionary mapping filenames to their full paths
1722
+ """
1723
+ import ast
1724
+
1725
+ discovered_files = {}
1726
+
1727
+ # Files that are handled specially by Golf and should not be auto-copied
1728
+ reserved_files = {
1729
+ "startup.py",
1730
+ "health.py",
1731
+ "readiness.py",
1732
+ "auth.py",
1733
+ "server.py",
1734
+ "pre_build.py", # Legacy auth file
1735
+ "golf.json",
1736
+ "golf.toml", # Config files
1737
+ "__init__.py", # Package files
1738
+ }
1739
+
1740
+ # Find all .py files in the project root (not in subdirectories)
1741
+ try:
1742
+ for file_path in project_path.iterdir():
1743
+ if not file_path.is_file():
1744
+ continue
1745
+
1746
+ filename = file_path.name
1747
+
1748
+ # Skip non-Python files
1749
+ if not filename.endswith(".py"):
1750
+ continue
1751
+
1752
+ # Skip reserved/special files
1753
+ if filename in reserved_files:
1754
+ continue
1755
+
1756
+ # Skip hidden files and temporary files
1757
+ if filename.startswith(".") or filename.startswith("_") or filename.endswith("~"):
1758
+ continue
1759
+
1760
+ # Validate it's a readable Python file
1761
+ try:
1762
+ with open(file_path, encoding="utf-8") as f:
1763
+ # Read first 200 chars to validate it's a proper Python file
1764
+ content = f.read(200)
1765
+
1766
+ # Basic Python syntax validation
1767
+ try:
1768
+ ast.parse(content + "\n# truncated for validation")
1769
+ except SyntaxError:
1770
+ # Still include files with syntax errors, but warn
1771
+ console.print(f"[yellow]Warning: {filename} has syntax issues but will be included[/yellow]")
1772
+
1773
+ except (OSError, UnicodeDecodeError) as e:
1774
+ console.print(f"[yellow]Warning: Cannot read {filename}, skipping: {e}[/yellow]")
1775
+ continue
1776
+
1777
+ discovered_files[filename] = file_path
1778
+
1779
+ except OSError as e:
1780
+ console.print(f"[yellow]Warning: Error scanning project directory: {e}[/yellow]")
1781
+
1782
+ if discovered_files:
1783
+ file_list = ", ".join(sorted(discovered_files.keys()))
1784
+ console.print(f"[dim]Found root Python files: {file_list}[/dim]")
1785
+
1786
+ return discovered_files
1787
+
1788
+
1712
1789
  # Legacy function removed - replaced by parse_shared_files in parser module
1713
1790
 
1714
1791
 
golf/core/builder_auth.py CHANGED
@@ -121,7 +121,7 @@ def generate_api_key_auth_components(
121
121
  "class ApiKeyMiddleware(BaseHTTPMiddleware):",
122
122
  " async def dispatch(self, request: Request, call_next):",
123
123
  " # Debug mode from environment",
124
- " debug = os.environ.get('GOLF_API_KEY_DEBUG', '').lower() == 'true'",
124
+ " debug = os.environ.get('API_KEY_DEBUG', '').lower() == 'true'",
125
125
  " ",
126
126
  " # Skip auth for monitoring endpoints",
127
127
  " path = request.url.path",
@@ -59,17 +59,6 @@ def generate_metrics_route(metrics_path: str) -> list[str]:
59
59
  ]
60
60
 
61
61
 
62
- def get_metrics_dependencies() -> list[str]:
63
- """Get list of metrics dependencies to add to pyproject.toml.
64
-
65
- Returns:
66
- List of package requirements strings
67
- """
68
- return [
69
- "prometheus-client>=0.19.0",
70
- ]
71
-
72
-
73
62
  def generate_metrics_instrumentation() -> list[str]:
74
63
  """Generate metrics instrumentation wrapper functions.
75
64
 
golf/core/config.py CHANGED
@@ -172,24 +172,11 @@ def load_settings(project_path: str | Path) -> Settings:
172
172
  if env_file.exists():
173
173
  settings = Settings(_env_file=env_file)
174
174
 
175
- # Auto-enable OpenTelemetry if GOLF_API_KEY is present (from .env file)
176
- import os
177
-
178
- if os.environ.get("GOLF_API_KEY"):
179
- settings.opentelemetry_enabled = True
180
-
181
175
  # Try to load JSON config file first
182
176
  json_config_path = project_path / "golf.json"
183
177
  if json_config_path.exists():
184
178
  return _load_json_settings(json_config_path, settings)
185
179
 
186
- # No config file found, use defaults
187
- # Auto-enable OpenTelemetry if GOLF_API_KEY is present
188
- import os
189
-
190
- if os.environ.get("GOLF_API_KEY"):
191
- settings.opentelemetry_enabled = True
192
-
193
180
  return settings
194
181
 
195
182
 
@@ -206,12 +193,6 @@ def _load_json_settings(path: Path, settings: Settings) -> Settings:
206
193
  if hasattr(settings, key):
207
194
  setattr(settings, key, value)
208
195
 
209
- # Auto-enable OpenTelemetry if GOLF_API_KEY is present and telemetry wasn't explicitly configured
210
- import os
211
-
212
- if os.environ.get("GOLF_API_KEY") and "opentelemetry_enabled" not in config_data:
213
- settings.opentelemetry_enabled = True
214
-
215
196
  return settings
216
197
  except Exception as e:
217
198
  console.print(f"[bold red]Error loading JSON config from {path}: {e}[/bold red]")
@@ -1,8 +1,4 @@
1
1
  # Golf MCP Server Environment Variables
2
2
 
3
- # Golf platform integration
4
- GOLF_API_KEY="your-golf-api-key"
5
- GOLF_SERVER_ID="your-server-id"
6
-
7
3
  # Development tokens are configured in auth.py for this example
8
4
  # Additional environment variables can be added as needed
@@ -14,13 +14,6 @@ from collections import OrderedDict
14
14
 
15
15
  from opentelemetry import baggage, trace
16
16
 
17
- # Import endpoints with fallback for dev mode
18
- try:
19
- # In built wheels, this exists (generated from _endpoints.py.in)
20
- from golf import _endpoints # type: ignore
21
- except ImportError:
22
- # In editable/dev installs, fall back to env-based values
23
- from golf import _endpoints_fallback as _endpoints # type: ignore
24
17
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
25
18
  from opentelemetry.sdk.resources import Resource
26
19
  from opentelemetry.sdk.trace import TracerProvider
@@ -69,28 +62,6 @@ def init_telemetry(service_name: str = "golf-mcp-server") -> TracerProvider | No
69
62
  """
70
63
  global _provider
71
64
 
72
- # Check for Golf platform integration first
73
- golf_api_key = os.environ.get("GOLF_API_KEY")
74
- if golf_api_key:
75
- # Auto-configure for Golf platform - always use OTLP when Golf API
76
- # key is present
77
- os.environ["OTEL_TRACES_EXPORTER"] = "otlp_http"
78
-
79
- # Only set endpoint if not already configured (allow user override)
80
- if not os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT"):
81
- os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = _endpoints.OTEL_ENDPOINT
82
-
83
- # Set Golf platform headers (append to existing if present)
84
- existing_headers = os.environ.get("OTEL_EXPORTER_OTLP_HEADERS", "")
85
- golf_header = f"X-Golf-Key={golf_api_key}"
86
-
87
- if existing_headers:
88
- # Check if Golf key is already in headers
89
- if "X-Golf-Key=" not in existing_headers:
90
- os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"{existing_headers},{golf_header}"
91
- else:
92
- os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = golf_header
93
-
94
65
  # Check for required environment variables based on exporter type
95
66
  exporter_type = os.environ.get("OTEL_TRACES_EXPORTER", "console").lower()
96
67
 
@@ -111,13 +82,6 @@ def init_telemetry(service_name: str = "golf-mcp-server") -> TracerProvider | No
111
82
  "service.instance.id": os.environ.get("SERVICE_INSTANCE_ID", "default"),
112
83
  }
113
84
 
114
- # Add Golf-specific attributes if available
115
- if golf_api_key:
116
- golf_server_id = os.environ.get("GOLF_SERVER_ID")
117
- if golf_server_id:
118
- resource_attributes["golf.server.id"] = golf_server_id
119
- resource_attributes["golf.platform.enabled"] = "true"
120
-
121
85
  resource = Resource.create(resource_attributes)
122
86
 
123
87
  # Create provider
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: golf-mcp
3
- Version: 0.2.11
3
+ Version: 0.2.13
4
4
  Summary: Framework for building MCP servers
5
5
  Author-email: Antoni Gmitruk <antoni@golf.dev>
6
6
  License-Expression: Apache-2.0
@@ -21,7 +21,7 @@ Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: typer>=0.15.4
23
23
  Requires-Dist: rich>=14.0.0
24
- Requires-Dist: fastmcp<3.0.0,>=2.11.0
24
+ Requires-Dist: fastmcp==2.12.5
25
25
  Requires-Dist: pydantic>=2.11.0
26
26
  Requires-Dist: pydantic-settings>=2.0.0
27
27
  Requires-Dist: python-dotenv>=1.1.0
@@ -197,9 +197,10 @@ from golf.utils import elicit, sample, get_context
197
197
  ```
198
198
 
199
199
  ```bash
200
- # Automatic telemetry with Golf Platform
201
- export GOLF_API_KEY="your-key"
202
- golf run # ✅ Telemetry enabled automatically
200
+ # Enable OpenTelemetry tracing
201
+ export OTEL_TRACES_EXPORTER="otlp_http"
202
+ export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318/v1/traces"
203
+ golf run # ✅ Telemetry enabled
203
204
  ```
204
205
 
205
206
  **[📚 Complete Documentation →](https://docs.golf.dev)**
@@ -220,7 +221,7 @@ Basic configuration in `golf.json`:
220
221
  ```
221
222
 
222
223
  - **`transport`**: Choose `"sse"`, `"streamable-http"`, or `"stdio"`
223
- - **`opentelemetry_enabled`**: Auto-enabled with `GOLF_API_KEY`
224
+ - **`opentelemetry_enabled`**: Enable OpenTelemetry tracing
224
225
  - **`detailed_tracing`**: Capture input/output (use carefully with sensitive data)
225
226
 
226
227
 
@@ -1,32 +1,28 @@
1
- golf/__init__.py,sha256=OpQ-bTTwRbO4KTjcnQgvU8ccnCWbcM-xPCnqQkEJNz0,307
2
- golf/_endpoints.py,sha256=-mvAmjx3YtqfAdajO13Kv7LKVBMdqsKqX0_3aCmCK4I,317
3
- golf/_endpoints.py.in,sha256=MeqcSF6xinnPknpBGF26xA9FYmSYKSWqCFXOSDlXiOA,216
4
- golf/_endpoints_fallback.py,sha256=CD6m45Ams1A9HVKowY8dCFUDMiFmJ8ZWSwHCENvU5u4,386
5
- golf/auth/__init__.py,sha256=gx62l8Z72eIldO2LtzKQ2OQVeobrhaSggTzJaZ0hcCo,8279
1
+ golf/__init__.py,sha256=C0atO05M0rfDTTHt02NxNa4jt0eSqXM4AxShEhb2epA,23
2
+ golf/auth/__init__.py,sha256=sYWjWXGR70Ae87znseeBK2YWl8S1-qn_hndKf2xMULk,8173
6
3
  golf/auth/api_key.py,sha256=OonqTlG6USJxqK8TlPviJTv7sgYmTVfCxG_JsEjnEM4,2320
7
4
  golf/auth/factory.py,sha256=3-il1GbjjZlQfvWUZs-09r61Y_-b5cYEegWF7sY_cUs,13128
8
- golf/auth/helpers.py,sha256=WoYyjUAQwgDnLvJDvyhuEOmm8w0fQ-ZDqaYxs_lC8nw,8127
5
+ golf/auth/helpers.py,sha256=N5NUGqlBg8rwfYX6zXmsWnjDE2Bh9n1CHeKAqMShToc,5589
9
6
  golf/auth/providers.py,sha256=f89WeQUrYopS0GBcTO3yXlqyPQvG7s7GpaiUK2tb2ME,25048
10
7
  golf/auth/registry.py,sha256=Rjj7LnWvzEsI1GCnFbFcxpRllrVanD9bumWPaJ1giFQ,7960
11
8
  golf/cli/__init__.py,sha256=R8Y8KdD2C8gDo24fXGq-fdWWNeaq3MYjrbaSB8Hb-Hg,45
12
- golf/cli/branding.py,sha256=ndpy2kVxBzqr4fwsAlh_fbhxqgRPoF6kM3ufP9hg5QI,6896
13
- golf/cli/main.py,sha256=0TAccbjB9wmB55GmCV3X1CpzaOoMSzpQ-bx2oxi-THw,13536
9
+ golf/cli/branding.py,sha256=OdmCaYVg6g3P8fqSNDlTG8R5CwUTuqSd68BrpPTjKp8,6861
10
+ golf/cli/main.py,sha256=YnppG_I15wfVU-dELgJbGTjESMvXGmBpUds1JkE83Es,13403
14
11
  golf/commands/__init__.py,sha256=j2r-0Zg2BSqG7WtZczAtyAsrur7kRQpEN9ZJuoX1XQ8,119
15
12
  golf/commands/build.py,sha256=sLq9lSW4naq2vIlBreKI5SGnviQrhBWw13zfOZOKhuM,2293
16
13
  golf/commands/init.py,sha256=KkAg_3-KxBDFOcZqUHqcPAkDipykFVaLWpQ2tydnVPk,9617
17
- golf/commands/run.py,sha256=a8GSwLt6E2cUJespv-u3jbD-rNUMHqF3VArD1uXV-Vk,4299
14
+ golf/commands/run.py,sha256=2fdjqTfzQL-dFOqptkl-K2jel8VWpevAx0somcjNsPI,4576
18
15
  golf/core/__init__.py,sha256=4bKeskJ2fPaZqkz2xQScSa3phRLLrmrczwSL632jv-o,52
19
- golf/core/builder.py,sha256=c0Phx77jkEJnks-FGN7ONuBmC5tQsVIHbwJxQkDAjKE,78180
20
- golf/core/builder_auth.py,sha256=SXPCpc5ipntoNqIAIA2ZCeGmEua6QVs7yC3MDtGKAro,8224
21
- golf/core/builder_metrics.py,sha256=j6Gtgd867o46JbDfSNGNsHt1QtV1XHKUJs1z8r4siQM,8830
16
+ golf/core/builder.py,sha256=7NuRoIGpmqT0vg2ZF5vMA-T6kBfcJOEi1oPwVUKJBes,80427
17
+ golf/core/builder_auth.py,sha256=f0w9TmxvNjxglcWY8NGULZJ8mwLqtUFFb2QHzgC0xAk,8219
18
+ golf/core/builder_metrics.py,sha256=wrXdE4_XWx7fUXnp61WU7rCBO-aG-4YzCMV6yO0z9Dg,8594
22
19
  golf/core/builder_telemetry.py,sha256=86bp7UlMUN6JyQRrZ5EizovP6AJ_q65OANJTeJXDIKc,3421
23
- golf/core/config.py,sha256=BD4bfyzIv1TZYDmBQTuzSXNF6aHfzWXY65LemAxdjNo,7510
20
+ golf/core/config.py,sha256=kL440b8i1dF8jdKeYwZZ04HdotX5CAlTyxR1ZyML-Iw,6850
24
21
  golf/core/parser.py,sha256=f9WqmLWlFuXQCObl2Qmna9bp_Bo0p0eIlukzwFaBSzo,43373
25
- golf/core/platform.py,sha256=0TxLfVPBhBR82aJAQWEJcnkGuQv65C9gYYy7P2foUVk,6662
26
22
  golf/core/telemetry.py,sha256=dXoWrgrQpj_HGrl_8TBZmRnuLxFKEn0GSDWQ9qq3ZQM,15686
27
23
  golf/core/transformer.py,sha256=iTDNFqdVeOjzl-acdjp8ZaTXss8Eptqu5cs4qLWt9SU,6914
28
24
  golf/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
- golf/examples/basic/.env.example,sha256=hxl1B9Vo7nCN5dWvTeN6BpOxTwHwPLEQzsnToHhx7nk,256
25
+ golf/examples/basic/.env.example,sha256=Ktdc2Qta05smPItYHnEHhVxYPkq5JJ7i2aI5pOyyQx4,162
30
26
  golf/examples/basic/README.md,sha256=p8KMyaNXGZtrbgckW-Thu_5RKXViGiyZ5hHEiJ6Zc-U,3283
31
27
  golf/examples/basic/auth.py,sha256=cdPWFU__i8Jw8xI1hgGSobhaC4YIBKsh5LsQY2GS2dw,2789
32
28
  golf/examples/basic/golf.json,sha256=327fO4XmvksmZdlYtYPmRcB6TgEVkD1nSvHAuGyWsYA,98
@@ -43,14 +39,14 @@ golf/metrics/__init__.py,sha256=O91y-hj_E9R06gqV8pDZrzHxOIl-1T415Hj9RvFAp3o,283
43
39
  golf/metrics/collector.py,sha256=ScKeOyDNawZRMIRYBFqNqmaYAIVJQ2riMu_qOE9ugvU,10967
44
40
  golf/metrics/registry.py,sha256=mXQE4Pwf3PopGYjcUu4eGgPDAe085YWcsvcvWk0ny8Q,310
45
41
  golf/telemetry/__init__.py,sha256=txSXDBhug5uVMD1KjZszcILMrVTjvTCWlxo7TWwbul8,506
46
- golf/telemetry/instrumentation.py,sha256=kLWkvw14eCUVawNtGuRRzEqaVcwzLJxk86uvDqW_tOU,57771
42
+ golf/telemetry/instrumentation.py,sha256=qBgSw8KqK_nroOjaeI1HMxaBUPsrIERyzp0ig842nu8,56159
47
43
  golf/utilities/__init__.py,sha256=X9iY9yi3agz1GVcn8-qWeOCt8CSSsruHxqPNtiF63TY,530
48
44
  golf/utilities/context.py,sha256=DGGvhVe---QMhy0wtdWhNp-_WVk1NvAcOFn0uBKBpYo,1579
49
45
  golf/utilities/elicitation.py,sha256=MParZZZsY45s70-KXduHa6IvpWXnLW2FCPfrGijMaHs,5223
50
46
  golf/utilities/sampling.py,sha256=88nDv-trBE4gZQbcnMjXl3LW6TiIhv5zR_cuEIGjaIM,7233
51
- golf_mcp-0.2.11.dist-info/licenses/LICENSE,sha256=5_j2f6fTJmvfmUewzElhkpAaXg2grVoxKouOA8ihV6E,11348
52
- golf_mcp-0.2.11.dist-info/METADATA,sha256=DDudTuflPauNfgHynFhldQLFI7MlJ-leZBZBhAdZpuY,9371
53
- golf_mcp-0.2.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
54
- golf_mcp-0.2.11.dist-info/entry_points.txt,sha256=5y7rHYM8jGpU-nfwdknCm5XsApLulqsnA37MO6BUTYg,43
55
- golf_mcp-0.2.11.dist-info/top_level.txt,sha256=BQToHcBUufdyhp9ONGMIvPE40jMEtmI20lYaKb4hxOg,5
56
- golf_mcp-0.2.11.dist-info/RECORD,,
47
+ golf_mcp-0.2.13.dist-info/licenses/LICENSE,sha256=5_j2f6fTJmvfmUewzElhkpAaXg2grVoxKouOA8ihV6E,11348
48
+ golf_mcp-0.2.13.dist-info/METADATA,sha256=P-2vXyg5M1YIAvvgMr7NBeViHlPSTs7wGpc0RUyUQys,9414
49
+ golf_mcp-0.2.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
50
+ golf_mcp-0.2.13.dist-info/entry_points.txt,sha256=5y7rHYM8jGpU-nfwdknCm5XsApLulqsnA37MO6BUTYg,43
51
+ golf_mcp-0.2.13.dist-info/top_level.txt,sha256=BQToHcBUufdyhp9ONGMIvPE40jMEtmI20lYaKb4hxOg,5
52
+ golf_mcp-0.2.13.dist-info/RECORD,,
golf/_endpoints.py DELETED
@@ -1,6 +0,0 @@
1
- # Auto-generated at build time by setup.py:build_py
2
- # This template contains placeholders that are replaced during build
3
-
4
- # Platform endpoints
5
- PLATFORM_API_URL = "https://golf-backend.golf-auth-1.authed-qukc4.ryvn.run/api/resources"
6
- OTEL_ENDPOINT = "https://golf-backend.golf-auth-1.authed-qukc4.ryvn.run/api/v1/otel"
golf/_endpoints.py.in DELETED
@@ -1,6 +0,0 @@
1
- # Auto-generated at build time by setup.py:build_py
2
- # This template contains placeholders that are replaced during build
3
-
4
- # Platform endpoints
5
- PLATFORM_API_URL = "{PLATFORM_API_URL}"
6
- OTEL_ENDPOINT = "{OTEL_ENDPOINT}"
@@ -1,10 +0,0 @@
1
- """Fallback endpoints for development/editable installs."""
2
-
3
- import os
4
-
5
- # These are used when the generated _endpoints.py doesn't exist (dev mode)
6
- # or when environment variables override the built-in values
7
-
8
- PLATFORM_API_URL = os.getenv("GOLF_PLATFORM_API_URL", "http://localhost:8000/api/resources")
9
-
10
- OTEL_ENDPOINT = os.getenv("GOLF_OTEL_ENDPOINT", "http://localhost:4318/v1/traces")
golf/core/platform.py DELETED
@@ -1,182 +0,0 @@
1
- """Platform registration for Golf MCP projects."""
2
-
3
- import os
4
- from datetime import datetime
5
- from pathlib import Path
6
- from typing import Any
7
-
8
- import httpx
9
- from rich.console import Console
10
-
11
- from golf import __version__
12
- from golf.core.config import Settings
13
- from golf.core.parser import ComponentType, ParsedComponent
14
-
15
- # Import endpoints with fallback for dev mode
16
- try:
17
- # In built wheels, this exists (generated from _endpoints.py.in)
18
- from golf import _endpoints # type: ignore
19
- except ImportError:
20
- # In editable/dev installs, fall back to env-based values
21
- from golf import _endpoints_fallback as _endpoints # type: ignore
22
-
23
- console = Console()
24
-
25
-
26
- async def register_project_with_platform(
27
- project_path: Path,
28
- settings: Settings,
29
- components: dict[ComponentType, list[ParsedComponent]],
30
- ) -> bool:
31
- """Register project with Golf platform during prod build.
32
-
33
- Args:
34
- project_path: Path to the project root
35
- settings: Project settings
36
- components: Parsed components dictionary
37
-
38
- Returns:
39
- True if registration succeeded or was skipped, False if failed
40
- """
41
- # Check if platform integration is enabled
42
- api_key = os.getenv("GOLF_API_KEY")
43
- if not api_key:
44
- return True # Skip silently if no API key
45
-
46
- # Require explicit server ID
47
- server_id = os.getenv("GOLF_SERVER_ID")
48
- if not server_id:
49
- console.print(
50
- "[yellow]Warning: Platform registration skipped - GOLF_SERVER_ID environment variable required[/yellow]"
51
- )
52
- return True # Skip registration but don't fail build
53
-
54
- # Build metadata payload
55
- metadata = {
56
- "project_name": settings.name,
57
- "description": settings.description,
58
- "server_id": server_id,
59
- "components": _build_component_list(components, project_path),
60
- "build_timestamp": datetime.utcnow().isoformat(),
61
- "golf_version": __version__,
62
- "component_counts": _get_component_counts(components),
63
- "server_config": {
64
- "host": settings.host,
65
- "port": settings.port,
66
- "transport": settings.transport,
67
- "auth_enabled": bool(settings.auth),
68
- "telemetry_enabled": settings.opentelemetry_enabled,
69
- "health_check_enabled": settings.health_check_enabled,
70
- },
71
- }
72
-
73
- try:
74
- async with httpx.AsyncClient(timeout=10.0) as client:
75
- response = await client.post(
76
- _endpoints.PLATFORM_API_URL,
77
- json=metadata,
78
- headers={
79
- "X-Golf-Key": api_key,
80
- "Content-Type": "application/json",
81
- "User-Agent": f"Golf-MCP/{__version__}",
82
- },
83
- )
84
- response.raise_for_status()
85
-
86
- console.print("[green]✓[/green] Registered with Golf platform")
87
- return True
88
-
89
- except httpx.TimeoutException:
90
- console.print("[yellow]Warning: Platform registration timed out[/yellow]")
91
- return False
92
- except httpx.HTTPStatusError as e:
93
- if e.response.status_code == 401:
94
- console.print("[yellow]Warning: Platform registration failed - invalid API key[/yellow]")
95
- elif e.response.status_code == 403:
96
- console.print("[yellow]Warning: Platform registration failed - access denied[/yellow]")
97
- else:
98
- console.print(f"[yellow]Warning: Platform registration failed - HTTP {e.response.status_code}[/yellow]")
99
- return False
100
- except Exception as e:
101
- console.print(f"[yellow]Warning: Platform registration failed: {e}[/yellow]")
102
- return False # Don't fail the build
103
-
104
-
105
- def _build_component_list(
106
- components: dict[ComponentType, list[ParsedComponent]],
107
- project_path: Path,
108
- ) -> list[dict[str, Any]]:
109
- """Convert parsed components to platform format.
110
-
111
- Args:
112
- components: Dictionary of parsed components by type
113
- project_path: Path to the project root
114
-
115
- Returns:
116
- List of component metadata dictionaries
117
- """
118
- result = []
119
-
120
- for comp_type, comp_list in components.items():
121
- for comp in comp_list:
122
- # Start with basic component data
123
- component_data = {
124
- "name": comp.name,
125
- "type": comp_type.value,
126
- "description": comp.docstring,
127
- "entry_function": comp.entry_function,
128
- "parent_module": comp.parent_module,
129
- }
130
-
131
- # Add file path relative to project root if available
132
- if comp.file_path:
133
- try:
134
- file_path = Path(comp.file_path)
135
- # Use the provided project_path for relative calculation
136
- rel_path = file_path.relative_to(project_path)
137
- component_data["file_path"] = str(rel_path)
138
- except ValueError:
139
- # If relative_to fails, try to find a common path or use filename
140
- component_data["file_path"] = Path(comp.file_path).name
141
-
142
- # Add schema information only if available and not None
143
- if hasattr(comp, "input_schema") and comp.input_schema:
144
- component_data["input_schema"] = comp.input_schema
145
- if hasattr(comp, "output_schema") and comp.output_schema:
146
- component_data["output_schema"] = comp.output_schema
147
-
148
- # Add component-specific fields only if they have values
149
- if comp_type == ComponentType.RESOURCE:
150
- if hasattr(comp, "uri_template") and comp.uri_template:
151
- component_data["uri_template"] = comp.uri_template
152
-
153
- elif comp_type == ComponentType.TOOL:
154
- if hasattr(comp, "annotations") and comp.annotations:
155
- component_data["annotations"] = comp.annotations
156
-
157
- # Add parameters only if they exist and are not empty
158
- if hasattr(comp, "parameters") and comp.parameters:
159
- component_data["parameters"] = comp.parameters
160
-
161
- result.append(component_data)
162
-
163
- return result
164
-
165
-
166
- def _get_component_counts(
167
- components: dict[ComponentType, list[ParsedComponent]],
168
- ) -> dict[str, int]:
169
- """Get component counts by type.
170
-
171
- Args:
172
- components: Dictionary of parsed components by type
173
-
174
- Returns:
175
- Dictionary with counts for each component type
176
- """
177
- return {
178
- "tools": len(components.get(ComponentType.TOOL, [])),
179
- "resources": len(components.get(ComponentType.RESOURCE, [])),
180
- "prompts": len(components.get(ComponentType.PROMPT, [])),
181
- "total": sum(len(comp_list) for comp_list in components.values()),
182
- }