golf-mcp 0.1.16__py3-none-any.whl → 0.1.18__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 +1 -1
- golf/cli/main.py +13 -2
- golf/commands/init.py +63 -1
- golf/core/builder.py +220 -59
- golf/core/builder_auth.py +5 -0
- golf/core/builder_metrics.py +232 -0
- golf/core/config.py +12 -0
- golf/core/parser.py +531 -32
- golf/core/platform.py +180 -0
- golf/core/telemetry.py +28 -8
- golf/examples/api_key/.env.example +1 -5
- golf/examples/api_key/README.md +10 -10
- golf/examples/api_key/golf.json +1 -5
- golf/examples/basic/.env.example +3 -4
- golf/examples/basic/golf.json +1 -5
- golf/metrics/__init__.py +10 -0
- golf/metrics/collector.py +239 -0
- golf/metrics/registry.py +12 -0
- golf/telemetry/instrumentation.py +177 -144
- {golf_mcp-0.1.16.dist-info → golf_mcp-0.1.18.dist-info}/METADATA +10 -3
- {golf_mcp-0.1.16.dist-info → golf_mcp-0.1.18.dist-info}/RECORD +25 -20
- {golf_mcp-0.1.16.dist-info → golf_mcp-0.1.18.dist-info}/WHEEL +0 -0
- {golf_mcp-0.1.16.dist-info → golf_mcp-0.1.18.dist-info}/entry_points.txt +0 -0
- {golf_mcp-0.1.16.dist-info → golf_mcp-0.1.18.dist-info}/licenses/LICENSE +0 -0
- {golf_mcp-0.1.16.dist-info → golf_mcp-0.1.18.dist-info}/top_level.txt +0 -0
golf/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.18"
|
golf/cli/main.py
CHANGED
|
@@ -121,7 +121,11 @@ def build_dev(
|
|
|
121
121
|
None, "--output-dir", "-o", help="Directory to output the built project"
|
|
122
122
|
),
|
|
123
123
|
) -> None:
|
|
124
|
-
"""Build a development version with environment variables copied.
|
|
124
|
+
"""Build a development version with app environment variables copied.
|
|
125
|
+
|
|
126
|
+
Golf credentials (GOLF_*) are always loaded from .env for build operations.
|
|
127
|
+
All environment variables are copied to the built project for development.
|
|
128
|
+
"""
|
|
125
129
|
# Find project root directory
|
|
126
130
|
project_root, config_path = find_project_root()
|
|
127
131
|
|
|
@@ -173,7 +177,14 @@ def build_prod(
|
|
|
173
177
|
None, "--output-dir", "-o", help="Directory to output the built project"
|
|
174
178
|
),
|
|
175
179
|
) -> None:
|
|
176
|
-
"""Build a production version
|
|
180
|
+
"""Build a production version for deployment.
|
|
181
|
+
|
|
182
|
+
Golf credentials (GOLF_*) are always loaded from .env for build operations
|
|
183
|
+
(platform registration, resource updates). App environment variables are
|
|
184
|
+
NOT copied for security - provide them in your deployment environment.
|
|
185
|
+
|
|
186
|
+
Your production deployment must include GOLF_* vars for runtime telemetry.
|
|
187
|
+
"""
|
|
177
188
|
# Find project root directory
|
|
178
189
|
project_root, config_path = find_project_root()
|
|
179
190
|
|
golf/commands/init.py
CHANGED
|
@@ -7,7 +7,12 @@ from rich.console import Console
|
|
|
7
7
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
8
8
|
from rich.prompt import Confirm
|
|
9
9
|
|
|
10
|
-
from golf.core.telemetry import
|
|
10
|
+
from golf.core.telemetry import (
|
|
11
|
+
track_command,
|
|
12
|
+
track_event,
|
|
13
|
+
set_telemetry_enabled,
|
|
14
|
+
load_telemetry_preference,
|
|
15
|
+
)
|
|
11
16
|
|
|
12
17
|
console = Console()
|
|
13
18
|
|
|
@@ -95,6 +100,9 @@ def initialize_project(
|
|
|
95
100
|
# Copy directory structure
|
|
96
101
|
_copy_template(template_dir, output_dir, project_name)
|
|
97
102
|
|
|
103
|
+
# Ask for telemetry consent
|
|
104
|
+
_prompt_for_telemetry_consent()
|
|
105
|
+
|
|
98
106
|
# Create virtual environment
|
|
99
107
|
console.print("[bold green]Project initialized successfully![/bold green]")
|
|
100
108
|
console.print("\nTo get started, run:")
|
|
@@ -207,6 +215,60 @@ def _copy_template(source_dir: Path, target_dir: Path, project_name: str) -> Non
|
|
|
207
215
|
f.write("dist/\n")
|
|
208
216
|
|
|
209
217
|
|
|
218
|
+
def _prompt_for_telemetry_consent() -> None:
|
|
219
|
+
"""Prompt user for telemetry consent and save their preference."""
|
|
220
|
+
import os
|
|
221
|
+
|
|
222
|
+
# Skip prompt in test mode, when telemetry is explicitly disabled, or if preference already exists
|
|
223
|
+
if os.environ.get("GOLF_TEST_MODE", "").lower() in ("1", "true", "yes", "on"):
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
# Skip if telemetry is explicitly disabled in environment
|
|
227
|
+
if os.environ.get("GOLF_TELEMETRY", "").lower() in ("0", "false", "no", "off"):
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
# Check if user already has a saved preference
|
|
231
|
+
existing_preference = load_telemetry_preference()
|
|
232
|
+
if existing_preference is not None:
|
|
233
|
+
return # User already made a choice
|
|
234
|
+
|
|
235
|
+
console.print()
|
|
236
|
+
console.rule("[bold blue]Anonymous usage analytics[/bold blue]", style="blue")
|
|
237
|
+
console.print()
|
|
238
|
+
console.print(
|
|
239
|
+
"Golf can collect [bold]anonymous usage analytics[/bold] to help improve the tool."
|
|
240
|
+
)
|
|
241
|
+
console.print()
|
|
242
|
+
console.print("[dim]What we collect:[/dim]")
|
|
243
|
+
console.print(" • Command usage (init, build, run)")
|
|
244
|
+
console.print(" • Error types (to fix bugs)")
|
|
245
|
+
console.print(" • Golf version and Python version")
|
|
246
|
+
console.print(" • Operating system type")
|
|
247
|
+
console.print()
|
|
248
|
+
console.print("[dim]What we DON'T collect:[/dim]")
|
|
249
|
+
console.print(" • Your code or project content")
|
|
250
|
+
console.print(" • File paths or project names")
|
|
251
|
+
console.print(" • Personal information")
|
|
252
|
+
console.print(" • IP addresses")
|
|
253
|
+
console.print()
|
|
254
|
+
console.print(
|
|
255
|
+
"You can change this anytime by setting GOLF_TELEMETRY=0 in your environment."
|
|
256
|
+
)
|
|
257
|
+
console.print()
|
|
258
|
+
|
|
259
|
+
enable_telemetry = Confirm.ask(
|
|
260
|
+
"[bold]Enable anonymous usage analytics?[/bold]", default=False
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
set_telemetry_enabled(enable_telemetry, persist=True)
|
|
264
|
+
|
|
265
|
+
if enable_telemetry:
|
|
266
|
+
console.print("[green]✓[/green] Anonymous analytics enabled")
|
|
267
|
+
else:
|
|
268
|
+
console.print("[yellow]○[/yellow] Anonymous analytics disabled")
|
|
269
|
+
console.print()
|
|
270
|
+
|
|
271
|
+
|
|
210
272
|
def _is_text_file(path: Path) -> bool:
|
|
211
273
|
"""Check if a file is a text file that needs variable substitution.
|
|
212
274
|
|
golf/core/builder.py
CHANGED
|
@@ -561,14 +561,18 @@ class CodeGenerator:
|
|
|
561
561
|
# Add OpenTelemetry imports if enabled
|
|
562
562
|
if self.settings.opentelemetry_enabled:
|
|
563
563
|
imports.extend(generate_telemetry_imports())
|
|
564
|
-
imports.append("")
|
|
565
564
|
|
|
566
|
-
# Add imports
|
|
567
|
-
if self.settings.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
565
|
+
# Add metrics imports if enabled
|
|
566
|
+
if self.settings.metrics_enabled:
|
|
567
|
+
from golf.core.builder_metrics import (
|
|
568
|
+
generate_metrics_imports,
|
|
569
|
+
generate_metrics_instrumentation,
|
|
570
|
+
generate_session_tracking,
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
imports.extend(generate_metrics_imports())
|
|
574
|
+
imports.extend(generate_metrics_instrumentation())
|
|
575
|
+
imports.extend(generate_session_tracking())
|
|
572
576
|
|
|
573
577
|
# Add health check imports if enabled
|
|
574
578
|
if self.settings.health_check_enabled:
|
|
@@ -664,7 +668,48 @@ class CodeGenerator:
|
|
|
664
668
|
# Add code to register this component
|
|
665
669
|
if self.settings.opentelemetry_enabled:
|
|
666
670
|
# Use telemetry instrumentation
|
|
667
|
-
registration =
|
|
671
|
+
registration = (
|
|
672
|
+
f"# Register the {component_type.value} "
|
|
673
|
+
f"'{component.name}' with telemetry"
|
|
674
|
+
)
|
|
675
|
+
entry_func = (
|
|
676
|
+
component.entry_function
|
|
677
|
+
if hasattr(component, "entry_function")
|
|
678
|
+
and component.entry_function
|
|
679
|
+
else "export"
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
registration += (
|
|
683
|
+
f"\n_wrapped_func = instrument_{component_type.value}("
|
|
684
|
+
f"{full_module_path}.{entry_func}, '{component.name}')"
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
if component_type == ComponentType.TOOL:
|
|
688
|
+
registration += (
|
|
689
|
+
f'\nmcp.add_tool(_wrapped_func, name="{component.name}", '
|
|
690
|
+
f'description="{component.docstring or ""}"'
|
|
691
|
+
)
|
|
692
|
+
# Add annotations if present
|
|
693
|
+
if hasattr(component, "annotations") and component.annotations:
|
|
694
|
+
registration += f", annotations={component.annotations}"
|
|
695
|
+
registration += ")"
|
|
696
|
+
elif component_type == ComponentType.RESOURCE:
|
|
697
|
+
registration += (
|
|
698
|
+
f"\nmcp.add_resource_fn(_wrapped_func, "
|
|
699
|
+
f'uri="{component.uri_template}", name="{component.name}", '
|
|
700
|
+
f'description="{component.docstring or ""}")'
|
|
701
|
+
)
|
|
702
|
+
else: # PROMPT
|
|
703
|
+
registration += (
|
|
704
|
+
f'\nmcp.add_prompt(_wrapped_func, name="{component.name}", '
|
|
705
|
+
f'description="{component.docstring or ""}")'
|
|
706
|
+
)
|
|
707
|
+
elif self.settings.metrics_enabled:
|
|
708
|
+
# Use metrics instrumentation
|
|
709
|
+
registration = (
|
|
710
|
+
f"# Register the {component_type.value} "
|
|
711
|
+
f"'{component.name}' with metrics"
|
|
712
|
+
)
|
|
668
713
|
entry_func = (
|
|
669
714
|
component.entry_function
|
|
670
715
|
if hasattr(component, "entry_function")
|
|
@@ -672,19 +717,31 @@ class CodeGenerator:
|
|
|
672
717
|
else "export"
|
|
673
718
|
)
|
|
674
719
|
|
|
675
|
-
|
|
676
|
-
|
|
720
|
+
registration += (
|
|
721
|
+
f"\n_wrapped_func = instrument_{component_type.value}("
|
|
722
|
+
f"{full_module_path}.{entry_func}, '{component.name}')"
|
|
723
|
+
)
|
|
677
724
|
|
|
678
725
|
if component_type == ComponentType.TOOL:
|
|
679
|
-
registration +=
|
|
726
|
+
registration += (
|
|
727
|
+
f'\nmcp.add_tool(_wrapped_func, name="{component.name}", '
|
|
728
|
+
f'description="{component.docstring or ""}"'
|
|
729
|
+
)
|
|
680
730
|
# Add annotations if present
|
|
681
731
|
if hasattr(component, "annotations") and component.annotations:
|
|
682
732
|
registration += f", annotations={component.annotations}"
|
|
683
733
|
registration += ")"
|
|
684
734
|
elif component_type == ComponentType.RESOURCE:
|
|
685
|
-
registration +=
|
|
735
|
+
registration += (
|
|
736
|
+
f"\nmcp.add_resource_fn(_wrapped_func, "
|
|
737
|
+
f'uri="{component.uri_template}", name="{component.name}", '
|
|
738
|
+
f'description="{component.docstring or ""}")'
|
|
739
|
+
)
|
|
686
740
|
else: # PROMPT
|
|
687
|
-
registration +=
|
|
741
|
+
registration += (
|
|
742
|
+
f'\nmcp.add_prompt(_wrapped_func, name="{component.name}", '
|
|
743
|
+
f'description="{component.docstring or ""}")'
|
|
744
|
+
)
|
|
688
745
|
else:
|
|
689
746
|
# Standard registration without telemetry
|
|
690
747
|
if component_type == ComponentType.TOOL:
|
|
@@ -795,6 +852,10 @@ class CodeGenerator:
|
|
|
795
852
|
for key, value in auth_components["fastmcp_args"].items():
|
|
796
853
|
mcp_constructor_args.append(f"{key}={value}")
|
|
797
854
|
|
|
855
|
+
# Add stateless HTTP parameter if enabled
|
|
856
|
+
if self.settings.stateless_http:
|
|
857
|
+
mcp_constructor_args.append("stateless_http=True")
|
|
858
|
+
|
|
798
859
|
# Add OpenTelemetry parameters if enabled
|
|
799
860
|
if self.settings.opentelemetry_enabled:
|
|
800
861
|
mcp_constructor_args.append("lifespan=telemetry_lifespan")
|
|
@@ -803,6 +864,27 @@ class CodeGenerator:
|
|
|
803
864
|
server_code_lines.append(mcp_instance_line)
|
|
804
865
|
server_code_lines.append("")
|
|
805
866
|
|
|
867
|
+
# Add early telemetry initialization if enabled (before component registration)
|
|
868
|
+
early_telemetry_init = []
|
|
869
|
+
if self.settings.opentelemetry_enabled:
|
|
870
|
+
early_telemetry_init.extend(
|
|
871
|
+
[
|
|
872
|
+
"# Initialize telemetry early to ensure instrumentation works",
|
|
873
|
+
"from golf.telemetry.instrumentation import init_telemetry",
|
|
874
|
+
f'init_telemetry("{self.settings.name}")',
|
|
875
|
+
"",
|
|
876
|
+
]
|
|
877
|
+
)
|
|
878
|
+
|
|
879
|
+
# Add metrics initialization if enabled
|
|
880
|
+
early_metrics_init = []
|
|
881
|
+
if self.settings.metrics_enabled:
|
|
882
|
+
from golf.core.builder_metrics import generate_metrics_initialization
|
|
883
|
+
|
|
884
|
+
early_metrics_init.extend(
|
|
885
|
+
generate_metrics_initialization(self.settings.name)
|
|
886
|
+
)
|
|
887
|
+
|
|
806
888
|
# Main entry point with transport-specific app initialization
|
|
807
889
|
main_code = [
|
|
808
890
|
'if __name__ == "__main__":',
|
|
@@ -830,77 +912,111 @@ class CodeGenerator:
|
|
|
830
912
|
|
|
831
913
|
# Transport-specific run methods
|
|
832
914
|
if self.settings.transport == "sse":
|
|
833
|
-
# Check if we need
|
|
915
|
+
# Check if we need middleware for SSE
|
|
916
|
+
middleware_setup = []
|
|
917
|
+
middleware_list = []
|
|
918
|
+
|
|
834
919
|
api_key_config = get_api_key_config()
|
|
835
920
|
if auth_components.get("has_auth") and api_key_config:
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
" # For SSE with API key auth, we need to get the app and add middleware",
|
|
839
|
-
' app = mcp.http_app(transport="sse")',
|
|
840
|
-
" app.add_middleware(ApiKeyMiddleware)",
|
|
841
|
-
]
|
|
921
|
+
middleware_setup.append(
|
|
922
|
+
" from starlette.middleware import Middleware"
|
|
842
923
|
)
|
|
843
|
-
|
|
924
|
+
middleware_list.append("Middleware(ApiKeyMiddleware)")
|
|
925
|
+
|
|
926
|
+
# Add metrics middleware if enabled
|
|
927
|
+
if self.settings.metrics_enabled:
|
|
928
|
+
middleware_setup.append(
|
|
929
|
+
" from starlette.middleware import Middleware"
|
|
930
|
+
)
|
|
931
|
+
middleware_list.append("Middleware(MetricsMiddleware)")
|
|
932
|
+
|
|
933
|
+
# Add OpenTelemetry middleware if enabled
|
|
934
|
+
if self.settings.opentelemetry_enabled:
|
|
935
|
+
middleware_setup.append(
|
|
936
|
+
" from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware"
|
|
937
|
+
)
|
|
938
|
+
middleware_setup.append(
|
|
939
|
+
" from starlette.middleware import Middleware"
|
|
940
|
+
)
|
|
941
|
+
middleware_list.append("Middleware(OpenTelemetryMiddleware)")
|
|
942
|
+
|
|
943
|
+
if middleware_setup:
|
|
944
|
+
main_code.extend(middleware_setup)
|
|
945
|
+
main_code.append(f" middleware = [{', '.join(middleware_list)}]")
|
|
946
|
+
main_code.append("")
|
|
844
947
|
main_code.extend(
|
|
845
948
|
[
|
|
846
|
-
" #
|
|
847
|
-
'
|
|
949
|
+
" # Run SSE server with middleware using FastMCP's run method",
|
|
950
|
+
' mcp.run(transport="sse", host=host, port=port, log_level="info", middleware=middleware)',
|
|
848
951
|
]
|
|
849
952
|
)
|
|
850
|
-
|
|
851
|
-
# Add OpenTelemetry middleware to the SSE app if enabled
|
|
852
|
-
if self.settings.opentelemetry_enabled:
|
|
953
|
+
else:
|
|
853
954
|
main_code.extend(
|
|
854
955
|
[
|
|
855
|
-
" #
|
|
856
|
-
|
|
857
|
-
" app = OpenTelemetryMiddleware(app)",
|
|
956
|
+
" # Run SSE server using FastMCP's run method",
|
|
957
|
+
' mcp.run(transport="sse", host=host, port=port, log_level="info")',
|
|
858
958
|
]
|
|
859
959
|
)
|
|
860
960
|
|
|
861
|
-
main_code.extend(
|
|
862
|
-
[
|
|
863
|
-
" # Run with the configured app",
|
|
864
|
-
' uvicorn.run(app, host=host, port=port, log_level="info")',
|
|
865
|
-
]
|
|
866
|
-
)
|
|
867
961
|
elif self.settings.transport in ["streamable-http", "http"]:
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
" app = mcp.http_app()",
|
|
872
|
-
]
|
|
873
|
-
)
|
|
962
|
+
# Check if we need middleware for streamable-http
|
|
963
|
+
middleware_setup = []
|
|
964
|
+
middleware_list = []
|
|
874
965
|
|
|
875
|
-
# Check if we need to add API key middleware
|
|
876
966
|
api_key_config = get_api_key_config()
|
|
877
967
|
if auth_components.get("has_auth") and api_key_config:
|
|
968
|
+
middleware_setup.append(
|
|
969
|
+
" from starlette.middleware import Middleware"
|
|
970
|
+
)
|
|
971
|
+
middleware_list.append("Middleware(ApiKeyMiddleware)")
|
|
972
|
+
|
|
973
|
+
# Add metrics middleware if enabled
|
|
974
|
+
if self.settings.metrics_enabled:
|
|
975
|
+
middleware_setup.append(
|
|
976
|
+
" from starlette.middleware import Middleware"
|
|
977
|
+
)
|
|
978
|
+
middleware_list.append("Middleware(MetricsMiddleware)")
|
|
979
|
+
|
|
980
|
+
# Add OpenTelemetry middleware if enabled
|
|
981
|
+
if self.settings.opentelemetry_enabled:
|
|
982
|
+
middleware_setup.append(
|
|
983
|
+
" from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware"
|
|
984
|
+
)
|
|
985
|
+
middleware_setup.append(
|
|
986
|
+
" from starlette.middleware import Middleware"
|
|
987
|
+
)
|
|
988
|
+
middleware_list.append("Middleware(OpenTelemetryMiddleware)")
|
|
989
|
+
|
|
990
|
+
if middleware_setup:
|
|
991
|
+
main_code.extend(middleware_setup)
|
|
992
|
+
main_code.append(f" middleware = [{', '.join(middleware_list)}]")
|
|
993
|
+
main_code.append("")
|
|
878
994
|
main_code.extend(
|
|
879
995
|
[
|
|
880
|
-
" #
|
|
881
|
-
|
|
996
|
+
" # Run HTTP server with middleware using FastMCP's run method",
|
|
997
|
+
' mcp.run(transport="streamable-http", host=host, port=port, log_level="info", middleware=middleware)',
|
|
882
998
|
]
|
|
883
999
|
)
|
|
884
|
-
|
|
885
|
-
# Add OpenTelemetry middleware to the HTTP app if enabled
|
|
886
|
-
if self.settings.opentelemetry_enabled:
|
|
1000
|
+
else:
|
|
887
1001
|
main_code.extend(
|
|
888
1002
|
[
|
|
889
|
-
" #
|
|
890
|
-
|
|
891
|
-
" app = OpenTelemetryMiddleware(app)",
|
|
1003
|
+
" # Run HTTP server using FastMCP's run method",
|
|
1004
|
+
' mcp.run(transport="streamable-http", host=host, port=port, log_level="info")',
|
|
892
1005
|
]
|
|
893
1006
|
)
|
|
894
|
-
|
|
895
|
-
main_code.extend(
|
|
896
|
-
[' uvicorn.run(app, host=host, port=port, log_level="info")']
|
|
897
|
-
)
|
|
898
1007
|
else:
|
|
899
1008
|
# For stdio transport, use mcp.run()
|
|
900
1009
|
main_code.extend(
|
|
901
1010
|
[" # Run with stdio transport", ' mcp.run(transport="stdio")']
|
|
902
1011
|
)
|
|
903
1012
|
|
|
1013
|
+
# Add metrics route if enabled
|
|
1014
|
+
metrics_route_code = []
|
|
1015
|
+
if self.settings.metrics_enabled:
|
|
1016
|
+
from golf.core.builder_metrics import generate_metrics_route
|
|
1017
|
+
|
|
1018
|
+
metrics_route_code = generate_metrics_route(self.settings.metrics_path)
|
|
1019
|
+
|
|
904
1020
|
# Add health check route if enabled
|
|
905
1021
|
health_check_code = []
|
|
906
1022
|
if self.settings.health_check_enabled:
|
|
@@ -917,13 +1033,16 @@ class CodeGenerator:
|
|
|
917
1033
|
|
|
918
1034
|
# Combine all sections
|
|
919
1035
|
# Order: imports, env_section, auth_setup, server_code (mcp init),
|
|
920
|
-
#
|
|
1036
|
+
# early_telemetry_init, early_metrics_init, component_registrations, metrics_route_code, health_check_code, main_code (run block)
|
|
921
1037
|
code = "\n".join(
|
|
922
1038
|
imports
|
|
923
1039
|
+ env_section
|
|
924
1040
|
+ auth_setup_code
|
|
925
1041
|
+ server_code_lines
|
|
1042
|
+
+ early_telemetry_init
|
|
1043
|
+
+ early_metrics_init
|
|
926
1044
|
+ component_registrations
|
|
1045
|
+
+ metrics_route_code
|
|
927
1046
|
+ health_check_code
|
|
928
1047
|
+ main_code
|
|
929
1048
|
)
|
|
@@ -955,6 +1074,21 @@ def build_project(
|
|
|
955
1074
|
build_env: Build environment ('dev' or 'prod')
|
|
956
1075
|
copy_env: Whether to copy environment variables to the built app
|
|
957
1076
|
"""
|
|
1077
|
+
# Load Golf credentials from .env for build operations (platform registration, etc.)
|
|
1078
|
+
# This happens regardless of copy_env setting to ensure build process works
|
|
1079
|
+
from dotenv import load_dotenv
|
|
1080
|
+
|
|
1081
|
+
project_env_file = project_path / ".env"
|
|
1082
|
+
if project_env_file.exists():
|
|
1083
|
+
# Load GOLF_* variables for build process
|
|
1084
|
+
load_dotenv(project_env_file, override=False)
|
|
1085
|
+
|
|
1086
|
+
# Only log if we actually found the specific Golf platform credentials
|
|
1087
|
+
has_api_key = "GOLF_API_KEY" in os.environ
|
|
1088
|
+
has_server_id = "GOLF_SERVER_ID" in os.environ
|
|
1089
|
+
if has_api_key and has_server_id:
|
|
1090
|
+
console.print("[dim]Loaded Golf credentials for build operations[/dim]")
|
|
1091
|
+
|
|
958
1092
|
# Execute pre_build.py if it exists
|
|
959
1093
|
pre_build_path = project_path / "pre_build.py"
|
|
960
1094
|
if pre_build_path.exists():
|
|
@@ -1056,9 +1190,6 @@ def build_project(
|
|
|
1056
1190
|
env_vars_to_write["OTEL_TRACES_EXPORTER"] = (
|
|
1057
1191
|
settings.opentelemetry_default_exporter
|
|
1058
1192
|
)
|
|
1059
|
-
console.print(
|
|
1060
|
-
f"[info]Setting OTEL_TRACES_EXPORTER to '{settings.opentelemetry_default_exporter}' from golf.json in built app's .env[/info]"
|
|
1061
|
-
)
|
|
1062
1193
|
|
|
1063
1194
|
# 3. Apply Golf's project name as OTEL_SERVICE_NAME if not already set
|
|
1064
1195
|
# (Ensures service name defaults to project name if not specified in user's .env)
|
|
@@ -1110,6 +1241,36 @@ def build_project(
|
|
|
1110
1241
|
)
|
|
1111
1242
|
generator.generate()
|
|
1112
1243
|
|
|
1244
|
+
# Platform registration (only for prod builds)
|
|
1245
|
+
if build_env == "prod":
|
|
1246
|
+
console.print(
|
|
1247
|
+
"[dim]Registering with Golf platform and updating resources...[/dim]"
|
|
1248
|
+
)
|
|
1249
|
+
import asyncio
|
|
1250
|
+
|
|
1251
|
+
try:
|
|
1252
|
+
from golf.core.platform import register_project_with_platform
|
|
1253
|
+
|
|
1254
|
+
asyncio.run(
|
|
1255
|
+
register_project_with_platform(
|
|
1256
|
+
project_path=project_path,
|
|
1257
|
+
settings=settings,
|
|
1258
|
+
components=generator.components,
|
|
1259
|
+
)
|
|
1260
|
+
)
|
|
1261
|
+
console.print("[green]✓ Platform registration completed[/green]")
|
|
1262
|
+
except ImportError:
|
|
1263
|
+
console.print(
|
|
1264
|
+
"[yellow]Warning: Platform registration module not available[/yellow]"
|
|
1265
|
+
)
|
|
1266
|
+
except Exception as e:
|
|
1267
|
+
console.print(
|
|
1268
|
+
f"[yellow]Warning: Platform registration failed: {e}[/yellow]"
|
|
1269
|
+
)
|
|
1270
|
+
console.print(
|
|
1271
|
+
"[yellow]Tip: Ensure GOLF_API_KEY and GOLF_SERVER_ID are available in your .env file[/yellow]"
|
|
1272
|
+
)
|
|
1273
|
+
|
|
1113
1274
|
# Create a simple README
|
|
1114
1275
|
readme_content = f"""# {settings.name}
|
|
1115
1276
|
|
|
@@ -1130,7 +1291,7 @@ This is a standalone FastMCP server generated by GolfMCP.
|
|
|
1130
1291
|
|
|
1131
1292
|
# Copy pyproject.toml with required dependencies
|
|
1132
1293
|
base_dependencies = [
|
|
1133
|
-
"fastmcp>=2.0.0",
|
|
1294
|
+
"fastmcp>=2.0.0,<2.6.0",
|
|
1134
1295
|
"uvicorn>=0.20.0",
|
|
1135
1296
|
"pydantic>=2.0.0",
|
|
1136
1297
|
"python-dotenv>=1.0.0",
|
golf/core/builder_auth.py
CHANGED
|
@@ -187,6 +187,11 @@ def generate_api_key_auth_components(
|
|
|
187
187
|
" # Debug mode from environment",
|
|
188
188
|
" debug = os.environ.get('GOLF_API_KEY_DEBUG', '').lower() == 'true'",
|
|
189
189
|
" ",
|
|
190
|
+
" # Skip auth for monitoring endpoints",
|
|
191
|
+
" path = request.url.path",
|
|
192
|
+
" if path in ['/metrics', '/health']:",
|
|
193
|
+
" return await call_next(request)",
|
|
194
|
+
" ",
|
|
190
195
|
" api_key_config = get_api_key_config()",
|
|
191
196
|
" ",
|
|
192
197
|
" if api_key_config:",
|