golf-mcp 0.1.19__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of golf-mcp might be problematic. Click here for more details.

Files changed (123) hide show
  1. golf/__init__.py +9 -1
  2. golf/_endpoints.py +6 -0
  3. golf/_endpoints_fallback.py +10 -0
  4. golf/auth/__init__.py +188 -84
  5. golf/auth/api_key.py +6 -14
  6. golf/auth/factory.py +333 -0
  7. golf/auth/helpers.py +12 -42
  8. golf/auth/providers.py +396 -0
  9. golf/auth/registry.py +256 -0
  10. golf/cli/branding.py +192 -0
  11. golf/cli/main.py +28 -69
  12. golf/commands/__init__.py +2 -0
  13. golf/commands/build.py +4 -7
  14. golf/commands/init.py +30 -53
  15. golf/commands/run.py +50 -20
  16. golf/core/builder.py +356 -412
  17. golf/core/builder_auth.py +63 -144
  18. golf/core/builder_telemetry.py +26 -3
  19. golf/core/config.py +38 -59
  20. golf/core/parser.py +132 -139
  21. golf/core/platform.py +12 -10
  22. golf/core/telemetry.py +11 -19
  23. golf/core/transformer.py +38 -15
  24. golf/examples/__pycache__/__init__.cpython-311.pyc +0 -0
  25. golf/examples/basic/.coverage +0 -0
  26. golf/examples/basic/.env.example +8 -4
  27. golf/examples/basic/README.md +117 -45
  28. golf/examples/basic/__pycache__/auth.cpython-311.pyc +0 -0
  29. golf/examples/basic/auth.py +76 -0
  30. golf/examples/basic/golf.json +2 -5
  31. golf/examples/basic/htmlcov/.gitignore +2 -0
  32. golf/examples/basic/htmlcov/class_index.html +547 -0
  33. golf/examples/basic/htmlcov/coverage_html_cb_6fb7b396.js +733 -0
  34. golf/examples/basic/htmlcov/favicon_32_cb_58284776.png +0 -0
  35. golf/examples/basic/htmlcov/function_index.html +2091 -0
  36. golf/examples/basic/htmlcov/index.html +349 -0
  37. golf/examples/basic/htmlcov/keybd_closed_cb_ce680311.png +0 -0
  38. golf/examples/basic/htmlcov/status.json +1 -0
  39. golf/examples/basic/htmlcov/style_cb_8e611ae1.css +337 -0
  40. golf/examples/basic/htmlcov/z_1c9a91c0e91c8496___init___py.html +323 -0
  41. golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_api_key_py.html +170 -0
  42. golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_factory_py.html +430 -0
  43. golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_helpers_py.html +288 -0
  44. golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_providers_py.html +493 -0
  45. golf/examples/basic/htmlcov/z_1c9a91c0e91c8496_registry_py.html +353 -0
  46. golf/examples/basic/htmlcov/z_3ec3b3f490dc0950___init___py.html +120 -0
  47. golf/examples/basic/htmlcov/z_3ec3b3f490dc0950_instrumentation_py.html +1535 -0
  48. golf/examples/basic/htmlcov/z_4b8b9dd4ccccc5db___init___py.html +98 -0
  49. golf/examples/basic/htmlcov/z_4b8b9dd4ccccc5db_branding_py.html +289 -0
  50. golf/examples/basic/htmlcov/z_4b8b9dd4ccccc5db_main_py.html +476 -0
  51. golf/examples/basic/htmlcov/z_5a6c4e6bcc86fb2f___init___py.html +97 -0
  52. golf/examples/basic/htmlcov/z_6cadab9ec0df475d___init___py.html +102 -0
  53. golf/examples/basic/htmlcov/z_6cadab9ec0df475d_build_py.html +178 -0
  54. golf/examples/basic/htmlcov/z_6cadab9ec0df475d_init_py.html +387 -0
  55. golf/examples/basic/htmlcov/z_6cadab9ec0df475d_run_py.html +222 -0
  56. golf/examples/basic/htmlcov/z_6fcdee0582ba84e4___init___py.html +106 -0
  57. golf/examples/basic/htmlcov/z_6fcdee0582ba84e4__endpoints_fallback_py.html +107 -0
  58. golf/examples/basic/htmlcov/z_7ba499ed22986217___init___py.html +98 -0
  59. golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_auth_py.html +306 -0
  60. golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_metrics_py.html +329 -0
  61. golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_py.html +1471 -0
  62. golf/examples/basic/htmlcov/z_7ba499ed22986217_builder_telemetry_py.html +186 -0
  63. golf/examples/basic/htmlcov/z_7ba499ed22986217_config_py.html +315 -0
  64. golf/examples/basic/htmlcov/z_7ba499ed22986217_parser_py.html +1149 -0
  65. golf/examples/basic/htmlcov/z_7ba499ed22986217_platform_py.html +279 -0
  66. golf/examples/basic/htmlcov/z_7ba499ed22986217_telemetry_py.html +589 -0
  67. golf/examples/basic/htmlcov/z_7ba499ed22986217_transformer_py.html +286 -0
  68. golf/examples/basic/htmlcov/z_7d7da37693a43688___init___py.html +107 -0
  69. golf/examples/basic/htmlcov/z_7d7da37693a43688_collector_py.html +417 -0
  70. golf/examples/basic/htmlcov/z_7d7da37693a43688_registry_py.html +109 -0
  71. golf/examples/basic/htmlcov/z_abe733142b40ad4e___init___py.html +109 -0
  72. golf/examples/basic/htmlcov/z_abe733142b40ad4e_context_py.html +150 -0
  73. golf/examples/basic/htmlcov/z_abe733142b40ad4e_elicitation_py.html +267 -0
  74. golf/examples/basic/htmlcov/z_abe733142b40ad4e_sampling_py.html +318 -0
  75. golf/examples/basic/prompts/__pycache__/welcome.cpython-311.pyc +0 -0
  76. golf/examples/basic/prompts/welcome.py +3 -5
  77. golf/examples/basic/resources/__pycache__/current_time.cpython-311.pyc +0 -0
  78. golf/examples/basic/resources/__pycache__/info.cpython-311.pyc +0 -0
  79. golf/examples/basic/resources/current_time.py +5 -13
  80. golf/examples/basic/resources/weather/__pycache__/common.cpython-311.pyc +0 -0
  81. golf/examples/basic/resources/weather/__pycache__/current.cpython-311.pyc +0 -0
  82. golf/examples/basic/resources/weather/__pycache__/forecast.cpython-311.pyc +0 -0
  83. golf/examples/basic/resources/weather/city.py +46 -0
  84. golf/examples/basic/resources/weather/common.py +4 -11
  85. golf/examples/basic/resources/weather/current.py +5 -5
  86. golf/examples/basic/resources/weather/forecast.py +5 -5
  87. golf/examples/basic/tools/__pycache__/calculator.cpython-311.pyc +0 -0
  88. golf/examples/basic/tools/calculator.py +94 -0
  89. golf/examples/basic/tools/say/__pycache__/hello.cpython-311.pyc +0 -0
  90. golf/examples/basic/tools/say/hello.py +65 -0
  91. golf/metrics/collector.py +100 -19
  92. golf/telemetry/__init__.py +4 -0
  93. golf/telemetry/instrumentation.py +496 -174
  94. golf/utilities/__init__.py +12 -0
  95. golf/utilities/context.py +53 -0
  96. golf/utilities/elicitation.py +170 -0
  97. golf/utilities/sampling.py +221 -0
  98. {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/METADATA +56 -110
  99. golf_mcp-0.2.0.dist-info/RECORD +110 -0
  100. golf/auth/oauth.py +0 -861
  101. golf/auth/provider.py +0 -115
  102. golf/examples/api_key/.env +0 -2
  103. golf/examples/api_key/.env.example +0 -1
  104. golf/examples/api_key/README.md +0 -84
  105. golf/examples/api_key/golf.json +0 -8
  106. golf/examples/api_key/pre_build.py +0 -11
  107. golf/examples/api_key/tools/issues/create.py +0 -93
  108. golf/examples/api_key/tools/issues/list.py +0 -92
  109. golf/examples/api_key/tools/repos/list.py +0 -111
  110. golf/examples/api_key/tools/search/code.py +0 -106
  111. golf/examples/api_key/tools/users/get.py +0 -82
  112. golf/examples/basic/.env +0 -5
  113. golf/examples/basic/pre_build.py +0 -28
  114. golf/examples/basic/tools/github_user.py +0 -65
  115. golf/examples/basic/tools/hello.py +0 -34
  116. golf/examples/basic/tools/payments/charge.py +0 -70
  117. golf/examples/basic/tools/payments/common.py +0 -36
  118. golf/examples/basic/tools/payments/refund.py +0 -61
  119. golf_mcp-0.1.19.dist-info/RECORD +0 -60
  120. {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/WHEEL +0 -0
  121. {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
  122. {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
  123. {golf_mcp-0.1.19.dist-info → golf_mcp-0.2.0.dist-info}/top_level.txt +0 -0
golf/cli/branding.py ADDED
@@ -0,0 +1,192 @@
1
+ """Golf CLI branding and visual utilities."""
2
+
3
+ from rich.console import Console
4
+ from rich.panel import Panel
5
+ from rich.text import Text
6
+ from rich.align import Align
7
+
8
+ # Golf brand colors (official brand colors)
9
+ GOLF_BLUE = "#2969FD" # Primary blue from brand: rgb(41, 105, 253)
10
+ GOLF_ORANGE = "#F97728" # Secondary orange from brand: rgb(249, 119, 40)
11
+ GOLF_GREEN = "#10B981" # Success green
12
+ GOLF_WHITE = "#FFFFFF"
13
+
14
+ # Simple GolfMCP text logo
15
+ GOLF_LOGO = """
16
+ ██████╗ ██████╗ ██╗ ███████╗███╗ ███╗ ██████╗██████╗
17
+ ██╔════╝ ██╔═══██╗██║ ██╔════╝████╗ ████║██╔════╝██╔══██╗
18
+ ██║ ███╗██║ ██║██║ █████╗ ██╔████╔██║██║ ██████╔╝
19
+ ██║ ██║██║ ██║██║ ██╔══╝ ██║╚██╔╝██║██║ ██╔═══╝
20
+ ╚██████╔╝╚██████╔╝███████╗██║ ██║ ╚═╝ ██║╚██████╗██║
21
+ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝
22
+ """
23
+
24
+ # Simplified version for smaller spaces
25
+ GOLF_LOGO_SMALL = "Golf"
26
+
27
+ # Status icons with consistent styling
28
+ STATUS_ICONS = {
29
+ "success": "✓",
30
+ "error": "✗",
31
+ "warning": "⚠",
32
+ "info": "ℹ",
33
+ "building": "🔨",
34
+ "generating": "⚙️",
35
+ "packaging": "📦",
36
+ "platform": "⛳",
37
+ "server": "🚀",
38
+ "loading": "⭕",
39
+ }
40
+
41
+
42
+ def create_welcome_banner(version: str, console: Console) -> None:
43
+ """Create the main Golf welcome banner."""
44
+ # Create the logo with Golf in blue and MCP in orange
45
+ logo_lines = GOLF_LOGO.strip().split("\n")
46
+ logo_content = Text()
47
+
48
+ for line in logo_lines:
49
+ if line.strip(): # Only process non-empty lines
50
+ # Find where "MCP" starts (roughly at position 32 in the ASCII art)
51
+ golf_part = line[:32] # First part is "Golf"
52
+ mcp_part = line[32:] # Last part is "MCP"
53
+
54
+ logo_content.append(golf_part, style=f"bold {GOLF_BLUE}")
55
+ logo_content.append(mcp_part, style=f"bold {GOLF_ORANGE}")
56
+ logo_content.append("\n")
57
+
58
+ # Create version line
59
+ version_text = Text()
60
+ version_text.append("🚀 ", style=f"bold {GOLF_ORANGE}")
61
+ version_text.append(f"Golf v{version}", style=f"bold {GOLF_BLUE}")
62
+ version_text.append(" 🚀", style=f"bold {GOLF_ORANGE}")
63
+
64
+ # Create tagline
65
+ tagline_text = Text("✨ Easiest way to build production-ready MCP servers ✨", style="bold white")
66
+
67
+ # Create the full content using a renderable group approach
68
+ from rich.console import Group
69
+
70
+ content_group = Group(
71
+ Align.center(logo_content),
72
+ "", # Empty line for spacing
73
+ Align.center(version_text),
74
+ Align.center(tagline_text),
75
+ )
76
+
77
+ panel = Panel(
78
+ content_group,
79
+ border_style=GOLF_BLUE,
80
+ padding=(1, 2),
81
+ title="[bold]🏌️ Welcome to Golf 🏌️[/bold]",
82
+ title_align="center",
83
+ )
84
+
85
+ console.print(panel)
86
+
87
+
88
+ def create_command_header(title: str, subtitle: str = "", console: Console | None = None) -> None:
89
+ """Create a styled command header."""
90
+ if console is None:
91
+ console = Console()
92
+
93
+ header = Text()
94
+ header.append("🏌️ ", style=f"bold {GOLF_ORANGE}")
95
+ header.append(title, style=f"bold {GOLF_BLUE}")
96
+
97
+ if subtitle:
98
+ header.append(f" → {subtitle}", style=f"bold {GOLF_ORANGE}")
99
+
100
+ # Create a stylish panel for the header
101
+ panel = Panel(
102
+ Align.center(header),
103
+ border_style=GOLF_BLUE,
104
+ padding=(0, 2),
105
+ )
106
+
107
+ console.print(panel)
108
+
109
+
110
+ def create_success_message(message: str, console: Console | None = None) -> None:
111
+ """Create a styled success message."""
112
+ if console is None:
113
+ console = Console()
114
+
115
+ success_content = Text()
116
+ success_content.append("🎉 ", style=f"bold {GOLF_ORANGE}")
117
+ success_content.append(f"{STATUS_ICONS['success']} {message}", style=f"bold {GOLF_GREEN}")
118
+ success_content.append(" 🎉", style=f"bold {GOLF_ORANGE}")
119
+
120
+ success_panel = Panel(
121
+ Align.center(success_content),
122
+ border_style=GOLF_GREEN,
123
+ padding=(0, 2),
124
+ title="[bold green]SUCCESS[/bold green]",
125
+ title_align="center",
126
+ )
127
+ console.print(success_panel)
128
+
129
+
130
+ def create_info_panel(title: str, content: str, console: Console | None = None) -> None:
131
+ """Create a styled info panel."""
132
+ if console is None:
133
+ console = Console()
134
+
135
+ # Add some visual flair to the content
136
+ styled_content = Text()
137
+ for line in content.split("\n"):
138
+ if line.strip():
139
+ styled_content.append("▶ ", style=f"bold {GOLF_ORANGE}")
140
+ styled_content.append(line, style="bold white")
141
+ styled_content.append("\n")
142
+
143
+ panel = Panel(
144
+ styled_content,
145
+ title=f"[bold {GOLF_BLUE}]🔧 {title} 🔧[/bold {GOLF_BLUE}]",
146
+ border_style=GOLF_BLUE,
147
+ padding=(1, 2),
148
+ )
149
+ console.print(panel)
150
+
151
+
152
+ def get_status_text(status: str, message: str, style: str = "") -> Text:
153
+ """Get formatted status text with icon."""
154
+ icon = STATUS_ICONS.get(status, "•")
155
+ text = Text()
156
+
157
+ if status == "success":
158
+ text.append("🎉 ", style=f"bold {GOLF_ORANGE}")
159
+ text.append(f"{icon} {message}", style=f"bold {GOLF_GREEN}")
160
+ elif status == "error":
161
+ text.append("💥 ", style=f"bold {GOLF_ORANGE}")
162
+ text.append(f"{icon} {message}", style="bold red")
163
+ elif status == "warning":
164
+ text.append("⚡ ", style=f"bold {GOLF_ORANGE}")
165
+ text.append(f"{icon} {message}", style=f"bold {GOLF_ORANGE}")
166
+ elif status in ["building", "generating", "packaging", "platform"]:
167
+ text.append("🔥 ", style=f"bold {GOLF_ORANGE}")
168
+ text.append(f"{icon} {message}", style=f"bold {GOLF_BLUE}")
169
+ else:
170
+ text.append("💡 ", style=f"bold {GOLF_ORANGE}")
171
+ text.append(f"{icon} {message}", style=f"bold {GOLF_BLUE}")
172
+
173
+ return text
174
+
175
+
176
+ def create_build_header(project_name: str, environment: str, console: Console) -> None:
177
+ """Create a styled build process header."""
178
+ title = Text()
179
+ title.append("🔨 Building ", style=f"bold {GOLF_ORANGE}")
180
+ title.append(project_name, style=f"bold {GOLF_BLUE}")
181
+ title.append(f" ({environment} environment)", style=f"bold {GOLF_GREEN}")
182
+
183
+ # Create a flashy build panel
184
+ panel = Panel(
185
+ Align.center(title),
186
+ border_style=GOLF_ORANGE,
187
+ padding=(0, 2),
188
+ title="[bold]🚧 BUILD IN PROGRESS 🚧[/bold]",
189
+ title_align="center",
190
+ )
191
+
192
+ console.print(panel)
golf/cli/main.py CHANGED
@@ -6,9 +6,9 @@ from pathlib import Path
6
6
 
7
7
  import typer
8
8
  from rich.console import Console
9
- from rich.panel import Panel
10
9
 
11
10
  from golf import __version__
11
+ from golf.cli.branding import create_welcome_banner, create_command_header
12
12
  from golf.core.config import find_project_root, load_settings
13
13
  from golf.core.telemetry import (
14
14
  is_telemetry_enabled,
@@ -35,7 +35,7 @@ atexit.register(shutdown)
35
35
  def _version_callback(value: bool) -> None:
36
36
  """Print version and exit if --version flag is used."""
37
37
  if value:
38
- console.print(f"GolfMCP v{__version__}")
38
+ create_welcome_banner(__version__, console)
39
39
  raise typer.Exit()
40
40
 
41
41
 
@@ -49,9 +49,7 @@ def callback(
49
49
  callback=_version_callback,
50
50
  is_eager=True,
51
51
  ),
52
- verbose: bool = typer.Option(
53
- False, "--verbose", "-v", help="Increase verbosity of output."
54
- ),
52
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Increase verbosity of output."),
55
53
  no_telemetry: bool = typer.Option(
56
54
  False,
57
55
  "--no-telemetry",
@@ -77,26 +75,24 @@ def callback(
77
75
  # Set telemetry preference if flag is used (permanent)
78
76
  if no_telemetry:
79
77
  set_telemetry_enabled(False, persist=True)
80
- console.print(
81
- "[dim]Telemetry has been disabled. You can re-enable it with: golf telemetry enable[/dim]"
82
- )
78
+ console.print("[dim]Telemetry has been disabled. You can re-enable it with: golf telemetry enable[/dim]")
83
79
 
84
80
 
85
81
  @app.command()
86
82
  def init(
87
83
  project_name: str = typer.Argument(..., help="Name of the project to create"),
88
- output_dir: Path | None = typer.Option(
89
- None, "--output-dir", "-o", help="Directory to create the project in"
90
- ),
91
- template: str = typer.Option(
92
- "basic", "--template", "-t", help="Template to use (basic or api_key)"
93
- ),
84
+ output_dir: Path | None = typer.Option(None, "--output-dir", "-o", help="Directory to create the project in"),
94
85
  ) -> None:
95
86
  """Initialize a new GolfMCP project.
96
87
 
97
88
  Creates a new directory with the project scaffold, including
98
89
  examples for tools, resources, and prompts.
99
90
  """
91
+ # Show the Golf logo for project initialization
92
+ create_welcome_banner(__version__, console)
93
+ console.print()
94
+ create_command_header("Initialize Project", f"Creating {project_name}", console)
95
+
100
96
  # Import here to avoid circular imports
101
97
  from golf.commands.init import initialize_project
102
98
 
@@ -105,9 +101,7 @@ def init(
105
101
  output_dir = Path.cwd() / project_name
106
102
 
107
103
  # Execute the initialization command (it handles its own tracking)
108
- initialize_project(
109
- project_name=project_name, output_dir=output_dir, template=template
110
- )
104
+ initialize_project(project_name=project_name, output_dir=output_dir)
111
105
 
112
106
 
113
107
  # Create a build group with subcommands
@@ -117,9 +111,7 @@ app.add_typer(build_app, name="build")
117
111
 
118
112
  @build_app.command("dev")
119
113
  def build_dev(
120
- output_dir: str | None = typer.Option(
121
- None, "--output-dir", "-o", help="Directory to output the built project"
122
- ),
114
+ output_dir: str | None = typer.Option(None, "--output-dir", "-o", help="Directory to output the built project"),
123
115
  ) -> None:
124
116
  """Build a development version with app environment variables copied.
125
117
 
@@ -155,9 +147,7 @@ def build_dev(
155
147
  # Build the project with environment variables copied
156
148
  from golf.commands.build import build_project
157
149
 
158
- build_project(
159
- project_root, settings, output_dir, build_env="dev", copy_env=True
160
- )
150
+ build_project(project_root, settings, output_dir, build_env="dev", copy_env=True)
161
151
  # Track successful build with environment
162
152
  track_event("cli_build_success", {"success": True, "environment": "dev"})
163
153
  except Exception as e:
@@ -173,9 +163,7 @@ def build_dev(
173
163
 
174
164
  @build_app.command("prod")
175
165
  def build_prod(
176
- output_dir: str | None = typer.Option(
177
- None, "--output-dir", "-o", help="Directory to output the built project"
178
- ),
166
+ output_dir: str | None = typer.Option(None, "--output-dir", "-o", help="Directory to output the built project"),
179
167
  ) -> None:
180
168
  """Build a production version for deployment.
181
169
 
@@ -214,9 +202,7 @@ def build_prod(
214
202
  # Build the project without copying environment variables
215
203
  from golf.commands.build import build_project
216
204
 
217
- build_project(
218
- project_root, settings, output_dir, build_env="prod", copy_env=False
219
- )
205
+ build_project(project_root, settings, output_dir, build_env="prod", copy_env=False)
220
206
  # Track successful build with environment
221
207
  track_event("cli_build_success", {"success": True, "environment": "prod"})
222
208
  except Exception as e:
@@ -232,18 +218,10 @@ def build_prod(
232
218
 
233
219
  @app.command()
234
220
  def run(
235
- dist_dir: str | None = typer.Option(
236
- None, "--dist-dir", "-d", help="Directory containing the built server"
237
- ),
238
- host: str | None = typer.Option(
239
- None, "--host", "-h", help="Host to bind to (overrides settings)"
240
- ),
241
- port: int | None = typer.Option(
242
- None, "--port", "-p", help="Port to bind to (overrides settings)"
243
- ),
244
- build_first: bool = typer.Option(
245
- True, "--build/--no-build", help="Build the project before running"
246
- ),
221
+ dist_dir: str | None = typer.Option(None, "--dist-dir", "-d", help="Directory containing the built server"),
222
+ host: str | None = typer.Option(None, "--host", "-h", help="Host to bind to (overrides settings)"),
223
+ port: int | None = typer.Option(None, "--port", "-p", help="Port to bind to (overrides settings)"),
224
+ build_first: bool = typer.Option(True, "--build/--no-build", help="Build the project before running"),
247
225
  ) -> None:
248
226
  """Run the built FastMCP server.
249
227
 
@@ -277,9 +255,7 @@ def run(
277
255
  # Check if dist directory exists
278
256
  if not dist_dir.exists():
279
257
  if build_first:
280
- console.print(
281
- f"[yellow]Dist directory {dist_dir} not found. Building first...[/yellow]"
282
- )
258
+ console.print(f"[yellow]Dist directory {dist_dir} not found. Building first...[/yellow]")
283
259
  try:
284
260
  # Build the project
285
261
  from golf.commands.build import build_project
@@ -296,12 +272,8 @@ def run(
296
272
  )
297
273
  raise
298
274
  else:
299
- console.print(
300
- f"[bold red]Error: Dist directory {dist_dir} not found.[/bold red]"
301
- )
302
- console.print(
303
- "Run 'golf build' first or use --build to build automatically."
304
- )
275
+ console.print(f"[bold red]Error: Dist directory {dist_dir} not found.[/bold red]")
276
+ console.print("Run 'golf build' first or use --build to build automatically.")
305
277
  track_event(
306
278
  "cli_run_failed",
307
279
  {
@@ -355,7 +327,7 @@ def run(
355
327
  {
356
328
  "success": False,
357
329
  "error_type": "UnexpectedExit",
358
- "error_message": f"Server process exited unexpectedly with code {return_code}",
330
+ "error_message": (f"Server process exited unexpectedly with code {return_code}"),
359
331
  "exit_code": return_code,
360
332
  "operation": "server_process_execution",
361
333
  "context": "Server process terminated with unexpected exit code",
@@ -384,36 +356,23 @@ def telemetry(
384
356
  """Manage telemetry settings."""
385
357
  if action.lower() == "enable":
386
358
  set_telemetry_enabled(True, persist=True)
387
- console.print(
388
- "[green]✓[/green] Telemetry enabled. Thank you for helping improve Golf!"
389
- )
359
+ console.print("[green]✓[/green] Telemetry enabled. Thank you for helping improve Golf!")
390
360
  elif action.lower() == "disable":
391
361
  set_telemetry_enabled(False, persist=True)
392
- console.print(
393
- "[yellow]Telemetry disabled.[/yellow] You can re-enable it anytime with: golf telemetry enable"
394
- )
362
+ console.print("[yellow]Telemetry disabled.[/yellow] You can re-enable it anytime with: golf telemetry enable")
395
363
  else:
396
- console.print(
397
- f"[red]Unknown action '{action}'. Use 'enable' or 'disable'.[/red]"
398
- )
364
+ console.print(f"[red]Unknown action '{action}'. Use 'enable' or 'disable'.[/red]")
399
365
  raise typer.Exit(code=1)
400
366
 
401
367
 
402
368
  if __name__ == "__main__":
403
369
  # Show welcome banner when run directly
404
- console.print(
405
- Panel.fit(
406
- f"[bold green]GolfMCP[/bold green] v{__version__}\n"
407
- "[dim]A Pythonic framework for building MCP servers[/dim]",
408
- border_style="green",
409
- )
410
- )
370
+ create_welcome_banner(__version__, console)
411
371
 
412
372
  # Add telemetry notice if enabled
413
373
  if is_telemetry_enabled():
414
374
  console.print(
415
- "[dim]📊 Anonymous usage data is collected to improve Golf. "
416
- "Disable with: golf telemetry disable[/dim]\n"
375
+ "[dim]📊 Anonymous usage data is collected to improve Golf. Disable with: golf telemetry disable[/dim]\n"
417
376
  )
418
377
 
419
378
  # Run the CLI app
golf/commands/__init__.py CHANGED
@@ -1,3 +1,5 @@
1
1
  """GolfMCP command implementations."""
2
2
 
3
3
  from golf.commands import build, init, run
4
+
5
+ __all__ = ["build", "init", "run"]
golf/commands/build.py CHANGED
@@ -32,16 +32,13 @@ def build_project(
32
32
  copy_env: Whether to copy environment variables to the built app
33
33
  """
34
34
  # Call the centralized build function from core.builder
35
- core_build_project(
36
- project_path, settings, output_dir, build_env=build_env, copy_env=copy_env
37
- )
35
+ core_build_project(project_path, settings, output_dir, build_env=build_env, copy_env=copy_env)
38
36
 
39
37
 
40
- # Add a main section to run the build_project function when this module is executed directly
38
+ # Add a main section to run the build_project function when this module is
39
+ # executed directly
41
40
  if __name__ == "__main__":
42
- parser = argparse.ArgumentParser(
43
- description="Build a standalone FastMCP application"
44
- )
41
+ parser = argparse.ArgumentParser(description="Build a standalone FastMCP application")
45
42
  parser.add_argument(
46
43
  "--project-path",
47
44
  "-p",
golf/commands/init.py CHANGED
@@ -7,6 +7,13 @@ 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.cli.branding import (
11
+ create_success_message,
12
+ create_info_panel,
13
+ STATUS_ICONS,
14
+ GOLF_ORANGE,
15
+ )
16
+
10
17
  from golf.core.telemetry import (
11
18
  track_command,
12
19
  track_event,
@@ -20,35 +27,21 @@ console = Console()
20
27
  def initialize_project(
21
28
  project_name: str,
22
29
  output_dir: Path,
23
- template: str = "basic",
24
30
  ) -> None:
25
- """Initialize a new GolfMCP project with the specified template.
31
+ """Initialize a new GolfMCP project.
26
32
 
27
33
  Args:
28
34
  project_name: Name of the project
29
35
  output_dir: Directory where the project will be created
30
- template: Template to use (basic or api_key)
31
36
  """
32
37
  try:
33
- # Validate template
34
- valid_templates = ("basic", "api_key")
35
- if template not in valid_templates:
36
- console.print(f"[bold red]Error:[/bold red] Unknown template '{template}'")
37
- console.print(f"Available templates: {', '.join(valid_templates)}")
38
- track_command(
39
- "init",
40
- success=False,
41
- error_type="InvalidTemplate",
42
- error_message=f"Unknown template: {template}",
43
- )
44
- return
38
+ # Use the basic template by default
39
+ template = "basic"
45
40
 
46
41
  # Check if directory exists
47
42
  if output_dir.exists():
48
43
  if not output_dir.is_dir():
49
- console.print(
50
- f"[bold red]Error:[/bold red] '{output_dir}' exists but is not a directory."
51
- )
44
+ console.print(f"[bold red]Error:[/bold red] '{output_dir}' exists but is not a directory.")
52
45
  track_command(
53
46
  "init",
54
47
  success=False,
@@ -78,9 +71,7 @@ def initialize_project(
78
71
  template_dir = package_init_file.parent / "examples" / template
79
72
 
80
73
  if not template_dir.exists():
81
- console.print(
82
- f"[bold red]Error:[/bold red] Could not find template '{template}'"
83
- )
74
+ console.print(f"[bold red]Error:[/bold red] Could not find template '{template}'")
84
75
  track_command(
85
76
  "init",
86
77
  success=False,
@@ -92,7 +83,9 @@ def initialize_project(
92
83
  # Copy template files
93
84
  with Progress(
94
85
  SpinnerColumn(),
95
- TextColumn("[bold green]Creating project structure...[/bold green]"),
86
+ TextColumn(
87
+ f"[bold {GOLF_ORANGE}]{STATUS_ICONS['building']} Creating project structure...[/bold {GOLF_ORANGE}]"
88
+ ),
96
89
  transient=True,
97
90
  ) as progress:
98
91
  progress.add_task("copying", total=None)
@@ -103,11 +96,13 @@ def initialize_project(
103
96
  # Ask for telemetry consent
104
97
  _prompt_for_telemetry_consent()
105
98
 
106
- # Create virtual environment
107
- console.print("[bold green]Project initialized successfully![/bold green]")
108
- console.print("\nTo get started, run:")
109
- console.print(f" cd {output_dir.name}")
110
- console.print(" golf build dev")
99
+ # Show success message
100
+ console.print()
101
+ create_success_message("Project initialized successfully!", console)
102
+
103
+ # Show next steps
104
+ next_steps = f"cd {output_dir.name}\ngolf build dev\ngolf run"
105
+ create_info_panel("Next Steps", next_steps, console)
111
106
 
112
107
  # Track successful initialization
113
108
  track_event("cli_init_success", {"success": True, "template": template})
@@ -116,12 +111,8 @@ def initialize_project(
116
111
  error_type = type(e).__name__
117
112
  error_message = str(e)
118
113
 
119
- console.print(
120
- f"[bold red]Error during initialization:[/bold red] {error_message}"
121
- )
122
- track_command(
123
- "init", success=False, error_type=error_type, error_message=error_message
124
- )
114
+ console.print(f"[bold red]Error during initialization:[/bold red] {error_message}")
115
+ track_command("init", success=False, error_type=error_type, error_message=error_message)
125
116
 
126
117
  # Re-raise to maintain existing behavior
127
118
  raise
@@ -160,9 +151,7 @@ def _copy_template(source_dir: Path, target_dir: Path, project_name: str) -> Non
160
151
 
161
152
  # Replace template variables
162
153
  content = content.replace("{{project_name}}", project_name)
163
- content = content.replace(
164
- "{{project_name_lowercase}}", project_name.lower()
165
- )
154
+ content = content.replace("{{project_name_lowercase}}", project_name.lower())
166
155
 
167
156
  with open(target_path, "w", encoding="utf-8") as f:
168
157
  f.write(content)
@@ -170,13 +159,6 @@ def _copy_template(source_dir: Path, target_dir: Path, project_name: str) -> Non
170
159
  # Binary file, just copy
171
160
  shutil.copy2(source_path, target_path)
172
161
 
173
- # Create .env file
174
- env_file = target_dir / ".env"
175
- with open(env_file, "w", encoding="utf-8") as f:
176
- f.write(f"GOLF_NAME={project_name}\n")
177
- f.write("GOLF_HOST=127.0.0.1\n")
178
- f.write("GOLF_PORT=3000\n")
179
-
180
162
  # Create a .gitignore if it doesn't exist
181
163
  gitignore_file = target_dir / ".gitignore"
182
164
  if not gitignore_file.exists():
@@ -219,7 +201,8 @@ def _prompt_for_telemetry_consent() -> None:
219
201
  """Prompt user for telemetry consent and save their preference."""
220
202
  import os
221
203
 
222
- # Skip prompt in test mode, when telemetry is explicitly disabled, or if preference already exists
204
+ # Skip prompt in test mode, when telemetry is explicitly disabled, or if
205
+ # preference already exists
223
206
  if os.environ.get("GOLF_TEST_MODE", "").lower() in ("1", "true", "yes", "on"):
224
207
  return
225
208
 
@@ -235,9 +218,7 @@ def _prompt_for_telemetry_consent() -> None:
235
218
  console.print()
236
219
  console.rule("[bold blue]Anonymous usage analytics[/bold blue]", style="blue")
237
220
  console.print()
238
- console.print(
239
- "Golf can collect [bold]anonymous usage analytics[/bold] to help improve the tool."
240
- )
221
+ console.print("Golf can collect [bold]anonymous usage analytics[/bold] to help improve the tool.")
241
222
  console.print()
242
223
  console.print("[dim]What we collect:[/dim]")
243
224
  console.print(" • Command usage (init, build, run)")
@@ -251,14 +232,10 @@ def _prompt_for_telemetry_consent() -> None:
251
232
  console.print(" • Personal information")
252
233
  console.print(" • IP addresses")
253
234
  console.print()
254
- console.print(
255
- "You can change this anytime by setting GOLF_TELEMETRY=0 in your environment."
256
- )
235
+ console.print("You can change this anytime by setting GOLF_TELEMETRY=0 in your environment.")
257
236
  console.print()
258
237
 
259
- enable_telemetry = Confirm.ask(
260
- "[bold]Enable anonymous usage analytics?[/bold]", default=False
261
- )
238
+ enable_telemetry = Confirm.ask("[bold]Enable anonymous usage analytics?[/bold]", default=False)
262
239
 
263
240
  set_telemetry_enabled(enable_telemetry, persist=True)
264
241