golf-mcp 0.1.20__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 +355 -414
  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 +484 -178
  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.20.dist-info → golf_mcp-0.2.0.dist-info}/METADATA +51 -104
  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.20.dist-info/RECORD +0 -60
  120. {golf_mcp-0.1.20.dist-info → golf_mcp-0.2.0.dist-info}/WHEEL +0 -0
  121. {golf_mcp-0.1.20.dist-info → golf_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
  122. {golf_mcp-0.1.20.dist-info → golf_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
  123. {golf_mcp-0.1.20.dist-info → golf_mcp-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,12 @@
1
+ """Golf utilities for enhanced MCP tool development.
2
+
3
+ This module provides convenient utilities for Golf tool authors to access
4
+ advanced MCP features like elicitation and sampling without needing to
5
+ manage FastMCP Context objects directly.
6
+ """
7
+
8
+ from .elicitation import elicit, elicit_confirmation
9
+ from .sampling import sample, sample_structured, sample_with_context
10
+ from .context import get_current_context
11
+
12
+ __all__ = ["elicit", "elicit_confirmation", "sample", "sample_structured", "sample_with_context", "get_current_context"]
@@ -0,0 +1,53 @@
1
+ """Context utilities for Golf MCP tools.
2
+
3
+ This module provides utilities to access the current FastMCP Context
4
+ from within Golf tool functions.
5
+ """
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from fastmcp.server.context import Context
11
+
12
+
13
+ def get_current_context() -> "Context":
14
+ """Get the current FastMCP Context.
15
+
16
+ This function retrieves the current FastMCP Context that was injected
17
+ into the tool function. It works by importing the FastMCP context
18
+ utilities at runtime.
19
+
20
+ Returns:
21
+ The current FastMCP Context instance
22
+
23
+ Raises:
24
+ RuntimeError: If called outside of an MCP request context
25
+ ImportError: If FastMCP is not available
26
+
27
+ Example:
28
+ ```python
29
+ from golf.utilities import get_current_context
30
+
31
+ async def my_tool(data: str):
32
+ ctx = get_current_context()
33
+ await ctx.info(f"Processing: {data}")
34
+ return "done"
35
+ ```
36
+ """
37
+ try:
38
+ # Import FastMCP context utilities at runtime
39
+ from fastmcp.server.context import _current_context
40
+
41
+ # Get the current context from the context variable
42
+ context = _current_context.get(None)
43
+
44
+ if context is None:
45
+ raise RuntimeError(
46
+ "No FastMCP Context available. This function must be called "
47
+ "from within an MCP tool function that has context injection enabled."
48
+ )
49
+
50
+ return context
51
+
52
+ except ImportError as e:
53
+ raise ImportError("FastMCP is not available. Please ensure fastmcp>=2.11.0 is installed.") from e
@@ -0,0 +1,170 @@
1
+ """Elicitation utilities for Golf MCP tools.
2
+
3
+ This module provides simplified elicitation functions that Golf tool authors
4
+ can use without needing to manage FastMCP Context objects directly.
5
+ """
6
+
7
+ from typing import Any, TypeVar, overload
8
+ from collections.abc import Callable
9
+
10
+ from .context import get_current_context
11
+
12
+ T = TypeVar("T")
13
+
14
+ # Apply telemetry instrumentation if available
15
+ try:
16
+ from golf.telemetry import instrument_elicitation
17
+
18
+ _instrumentation_available = True
19
+ except ImportError:
20
+ _instrumentation_available = False
21
+
22
+ def instrument_elicitation(func: Callable, elicitation_type: str = "elicit") -> Callable:
23
+ """No-op instrumentation when telemetry is not available."""
24
+ return func
25
+
26
+
27
+ @overload
28
+ async def elicit(
29
+ message: str,
30
+ response_type: None = None,
31
+ ) -> dict[str, Any]:
32
+ """Elicit with no response type returns empty dict."""
33
+ ...
34
+
35
+
36
+ @overload
37
+ async def elicit(
38
+ message: str,
39
+ response_type: type[T],
40
+ ) -> T:
41
+ """Elicit with response type returns typed data."""
42
+ ...
43
+
44
+
45
+ @overload
46
+ async def elicit(
47
+ message: str,
48
+ response_type: list[str],
49
+ ) -> str:
50
+ """Elicit with list of options returns selected string."""
51
+ ...
52
+
53
+
54
+ async def elicit(
55
+ message: str,
56
+ response_type: type[T] | list[str] | None = None,
57
+ ) -> T | dict[str, Any] | str:
58
+ """Request additional information from the user via MCP elicitation.
59
+
60
+ This is a simplified wrapper around FastMCP's Context.elicit() method
61
+ that automatically handles context retrieval and response processing.
62
+
63
+ Args:
64
+ message: Human-readable message explaining what information is needed
65
+ response_type: The type of response expected:
66
+ - None: Returns empty dict (for confirmation prompts)
67
+ - type[T]: Returns validated instance of T (BaseModel, dataclass, etc.)
68
+ - list[str]: Returns selected string from the options
69
+
70
+ Returns:
71
+ The user's response in the requested format
72
+
73
+ Raises:
74
+ RuntimeError: If called outside MCP context or user declines/cancels
75
+ ValueError: If response validation fails
76
+
77
+ Examples:
78
+ ```python
79
+ from golf.utilities import elicit
80
+ from pydantic import BaseModel
81
+
82
+ class UserInfo(BaseModel):
83
+ name: str
84
+ email: str
85
+
86
+ async def collect_user_info():
87
+ # Structured elicitation
88
+ info = await elicit("Please provide your details:", UserInfo)
89
+
90
+ # Simple text elicitation
91
+ reason = await elicit("Why do you need this?", str)
92
+
93
+ # Multiple choice elicitation
94
+ priority = await elicit("Select priority:", ["low", "medium", "high"])
95
+
96
+ # Confirmation elicitation
97
+ await elicit("Proceed with the action?")
98
+
99
+ return f"User {info.name} requested {reason} with {priority} priority"
100
+ ```
101
+ """
102
+ try:
103
+ # Get the current FastMCP context
104
+ ctx = get_current_context()
105
+
106
+ # Call the context's elicit method
107
+ result = await ctx.elicit(message, response_type)
108
+
109
+ # Handle the response based on the action
110
+ if hasattr(result, "action"):
111
+ if result.action == "accept":
112
+ return result.data
113
+ elif result.action == "decline":
114
+ raise RuntimeError(f"User declined the elicitation request: {message}")
115
+ elif result.action == "cancel":
116
+ raise RuntimeError(f"User cancelled the elicitation request: {message}")
117
+ else:
118
+ raise RuntimeError(f"Unexpected elicitation response: {result.action}")
119
+ else:
120
+ # Direct response (shouldn't happen with current FastMCP)
121
+ return result
122
+
123
+ except Exception as e:
124
+ if isinstance(e, RuntimeError):
125
+ raise # Re-raise our custom errors
126
+ raise RuntimeError(f"Elicitation failed: {str(e)}") from e
127
+
128
+
129
+ async def elicit_confirmation(message: str) -> bool:
130
+ """Request a simple yes/no confirmation from the user.
131
+
132
+ This is a convenience function for common confirmation prompts.
133
+
134
+ Args:
135
+ message: The confirmation message to show the user
136
+
137
+ Returns:
138
+ True if user confirmed, False if declined
139
+
140
+ Raises:
141
+ RuntimeError: If user cancels or other error occurs
142
+
143
+ Example:
144
+ ```python
145
+ from golf.utilities import elicit_confirmation
146
+
147
+ async def delete_file(filename: str):
148
+ confirmed = await elicit_confirmation(
149
+ f"Are you sure you want to delete {filename}?"
150
+ )
151
+ if confirmed:
152
+ # Proceed with deletion
153
+ return f"Deleted {filename}"
154
+ else:
155
+ return "Deletion cancelled"
156
+ ```
157
+ """
158
+ try:
159
+ # Use elicitation with boolean choice
160
+ choice = await elicit(message, ["yes", "no"])
161
+ return choice.lower() == "yes"
162
+ except RuntimeError as e:
163
+ if "declined" in str(e):
164
+ return False
165
+ raise # Re-raise cancellation or other errors
166
+
167
+
168
+ # Apply instrumentation to all elicitation functions
169
+ elicit = instrument_elicitation(elicit, "elicit")
170
+ elicit_confirmation = instrument_elicitation(elicit_confirmation, "confirmation")
@@ -0,0 +1,221 @@
1
+ """Sampling utilities for Golf MCP tools.
2
+
3
+ This module provides simplified LLM sampling functions that Golf tool authors
4
+ can use without needing to manage FastMCP Context objects directly.
5
+ """
6
+
7
+ from typing import Any
8
+ from collections.abc import Callable
9
+
10
+ from .context import get_current_context
11
+
12
+ # Apply telemetry instrumentation if available
13
+ try:
14
+ from golf.telemetry import instrument_sampling
15
+
16
+ _instrumentation_available = True
17
+ except ImportError:
18
+ _instrumentation_available = False
19
+
20
+ def instrument_sampling(func: Callable, sampling_type: str = "sample") -> Callable:
21
+ """No-op instrumentation when telemetry is not available."""
22
+ return func
23
+
24
+
25
+ async def sample(
26
+ messages: str | list[str],
27
+ system_prompt: str | None = None,
28
+ temperature: float | None = None,
29
+ max_tokens: int | None = None,
30
+ model_preferences: str | list[str] | None = None,
31
+ ) -> str:
32
+ """Request an LLM completion from the MCP client.
33
+
34
+ This is a simplified wrapper around FastMCP's Context.sample() method
35
+ that automatically handles context retrieval and response processing.
36
+
37
+ Args:
38
+ messages: The message(s) to send to the LLM:
39
+ - str: Single user message
40
+ - list[str]: Multiple user messages
41
+ system_prompt: Optional system prompt to guide the LLM
42
+ temperature: Optional temperature for sampling (0.0 to 1.0)
43
+ max_tokens: Optional maximum tokens to generate (default: 512)
44
+ model_preferences: Optional model preferences:
45
+ - str: Single model name hint
46
+ - list[str]: Multiple model name hints in preference order
47
+
48
+ Returns:
49
+ The LLM's response as a string
50
+
51
+ Raises:
52
+ RuntimeError: If called outside MCP context or sampling fails
53
+ ValueError: If parameters are invalid
54
+
55
+ Examples:
56
+ ```python
57
+ from golf.utilities import sample
58
+
59
+ async def analyze_data(data: str):
60
+ # Simple completion
61
+ analysis = await sample(f"Analyze this data: {data}")
62
+
63
+ # With system prompt and temperature
64
+ creative_response = await sample(
65
+ "Write a creative story about this data",
66
+ system_prompt="You are a creative writer",
67
+ temperature=0.8,
68
+ max_tokens=1000
69
+ )
70
+
71
+ # With model preferences
72
+ technical_analysis = await sample(
73
+ f"Provide technical analysis: {data}",
74
+ model_preferences=["gpt-4", "claude-3-sonnet"]
75
+ )
76
+
77
+ return {
78
+ "analysis": analysis,
79
+ "creative": creative_response,
80
+ "technical": technical_analysis
81
+ }
82
+ ```
83
+ """
84
+ try:
85
+ # Get the current FastMCP context
86
+ ctx = get_current_context()
87
+
88
+ # Call the context's sample method
89
+ result = await ctx.sample(
90
+ messages=messages,
91
+ system_prompt=system_prompt,
92
+ temperature=temperature,
93
+ max_tokens=max_tokens,
94
+ model_preferences=model_preferences,
95
+ )
96
+
97
+ # Extract text content from the ContentBlock response
98
+ if hasattr(result, "text"):
99
+ return result.text
100
+ elif hasattr(result, "content"):
101
+ # Handle different content block types
102
+ if isinstance(result.content, str):
103
+ return result.content
104
+ elif hasattr(result.content, "text"):
105
+ return result.content.text
106
+ else:
107
+ return str(result.content)
108
+ else:
109
+ return str(result)
110
+
111
+ except Exception as e:
112
+ raise RuntimeError(f"LLM sampling failed: {str(e)}") from e
113
+
114
+
115
+ async def sample_structured(
116
+ messages: str | list[str],
117
+ format_instructions: str,
118
+ system_prompt: str | None = None,
119
+ temperature: float = 0.1,
120
+ max_tokens: int | None = None,
121
+ ) -> str:
122
+ """Request a structured LLM completion with specific formatting.
123
+
124
+ This is a convenience function for requesting structured responses
125
+ like JSON, XML, or other formatted output.
126
+
127
+ Args:
128
+ messages: The message(s) to send to the LLM
129
+ format_instructions: Instructions for the desired output format
130
+ system_prompt: Optional system prompt
131
+ temperature: Temperature for sampling (default: 0.1 for consistency)
132
+ max_tokens: Optional maximum tokens to generate
133
+
134
+ Returns:
135
+ The structured LLM response as a string
136
+
137
+ Example:
138
+ ```python
139
+ from golf.utilities import sample_structured
140
+
141
+ async def extract_entities(text: str):
142
+ entities = await sample_structured(
143
+ f"Extract entities from: {text}",
144
+ format_instructions="Return as JSON with keys: persons, "
145
+ "organizations, locations",
146
+ system_prompt="You are an expert at named entity recognition"
147
+ )
148
+ return entities
149
+ ```
150
+ """
151
+ # Combine the format instructions with the messages
152
+ if isinstance(messages, str):
153
+ formatted_message = f"{messages}\n\n{format_instructions}"
154
+ else:
155
+ formatted_message = messages + [format_instructions]
156
+
157
+ return await sample(
158
+ messages=formatted_message,
159
+ system_prompt=system_prompt,
160
+ temperature=temperature,
161
+ max_tokens=max_tokens,
162
+ )
163
+
164
+
165
+ async def sample_with_context(
166
+ messages: str | list[str],
167
+ context_data: dict[str, Any],
168
+ system_prompt: str | None = None,
169
+ **kwargs: Any,
170
+ ) -> str:
171
+ """Request an LLM completion with additional context data.
172
+
173
+ This convenience function formats context data and includes it
174
+ in the sampling request.
175
+
176
+ Args:
177
+ messages: The message(s) to send to the LLM
178
+ context_data: Dictionary of context data to include
179
+ system_prompt: Optional system prompt
180
+ **kwargs: Additional arguments passed to sample()
181
+
182
+ Returns:
183
+ The LLM response as a string
184
+
185
+ Example:
186
+ ```python
187
+ from golf.utilities import sample_with_context
188
+
189
+ async def generate_report(topic: str, user_data: dict):
190
+ report = await sample_with_context(
191
+ f"Generate a report about {topic}",
192
+ context_data={
193
+ "user_preferences": user_data,
194
+ "timestamp": "2024-01-01",
195
+ "format": "markdown"
196
+ },
197
+ system_prompt="You are a professional report writer"
198
+ )
199
+ return report
200
+ ```
201
+ """
202
+ # Format context data as a readable string
203
+ context_str = "\n".join([f"{k}: {v}" for k, v in context_data.items()])
204
+
205
+ # Add context to the message
206
+ if isinstance(messages, str):
207
+ contextual_message = f"{messages}\n\nContext:\n{context_str}"
208
+ else:
209
+ contextual_message = messages + [f"Context:\n{context_str}"]
210
+
211
+ return await sample(
212
+ messages=contextual_message,
213
+ system_prompt=system_prompt,
214
+ **kwargs,
215
+ )
216
+
217
+
218
+ # Apply instrumentation to all sampling functions
219
+ sample = instrument_sampling(sample, "sample")
220
+ sample_structured = instrument_sampling(sample_structured, "structured")
221
+ sample_with_context = instrument_sampling(sample_with_context, "context")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: golf-mcp
3
- Version: 0.1.20
3
+ Version: 0.2.0
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,8 +21,9 @@ 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<2.6.0,>=2.0.0
24
+ Requires-Dist: fastmcp<3.0.0,>=2.11.0
25
25
  Requires-Dist: pydantic>=2.11.0
26
+ Requires-Dist: pydantic-settings>=2.0.0
26
27
  Requires-Dist: python-dotenv>=1.1.0
27
28
  Requires-Dist: black>=24.10.0
28
29
  Requires-Dist: pyjwt>=2.0.0
@@ -67,9 +68,9 @@ Dynamic: license-file
67
68
 
68
69
  ## Overview
69
70
 
70
- Golf is a **framework** designed to streamline the creation of MCP server applications. It allows developers to define server's capabilities—*tools*, *prompts*, and *resources*—as simple Python files within a conventional directory structure. Golf then automatically discovers, parses, and compiles these components into a runnable FastMCP server, minimizing boilerplate and accelerating development.
71
+ Golf is a **framework** designed to streamline the creation of MCP server applications. It allows developers to define server's capabilities—*tools*, *prompts*, and *resources*—as simple Python files within a conventional directory structure. Golf then automatically discovers, parses, and compiles these components into a runnable MCP server, minimizing boilerplate and accelerating development.
71
72
 
72
- With Golf, you can focus on implementing your agent's logic rather than wrestling with server setup and integration complexities. It's built for developers who want a quick, organized way to build powerful MCP servers.
73
+ With Golf v0.2.0, you get **enterprise-grade authentication** (JWT, OAuth Server, API key, development tokens), **built-in utilities** for LLM interactions, and **automatic telemetry** integration. Focus on implementing your agent's logic while Golf handles authentication, monitoring, and server infrastructure.
73
74
 
74
75
  ## Quick Start
75
76
 
@@ -101,7 +102,7 @@ cd your-project-name
101
102
  golf build dev
102
103
  golf run
103
104
  ```
104
- This will start the FastMCP server, typically on `http://127.0.0.1:3000` (configurable in `golf.json`).
105
+ This will start the MCP server, typically on `http://localhost:3000` (configurable in `golf.json`).
105
106
 
106
107
  That's it! Your Golf server is running and ready for integration.
107
108
 
@@ -124,10 +125,11 @@ A Golf project initialized with `golf init` will have a structure similar to thi
124
125
  │ └─ welcome.py # Example prompt
125
126
 
126
127
  ├─ .env # Environment variables (e.g., API keys, server port)
127
- └─ pre_build.py # (Optional) Script for pre-build hooks (e.g., auth setup)
128
+ └─ auth.py # Authentication configuration (JWT, OAuth Server, API key, dev tokens)
128
129
  ```
129
130
 
130
131
  - **`golf.json`**: Configures server name, port, transport, telemetry, and other build settings.
132
+ - **`auth.py`**: Dedicated authentication configuration file (new in v0.2.0, breaking change from v0.1.x authentication API) for JWT, OAuth Server, API key, or development authentication.
131
133
  - **`tools/`**, **`resources/`**, **`prompts/`**: Contain your Python files, each defining a single component. These directories can also contain nested subdirectories to further organize your components (e.g., `tools/payments/charge.py`). The module docstring of each file serves as the component's description.
132
134
  - Component IDs are automatically derived from their file path. For example, `tools/hello.py` becomes `hello`, and a nested file like `tools/payments/submit.py` would become `submit_payments` (filename, followed by reversed parent directories under the main category, joined by underscores).
133
135
  - **`common.py`** (not shown, but can be placed in subdirectories like `tools/payments/common.py`): Used to share code (clients, models, etc.) among components in the same subdirectory.
@@ -164,118 +166,63 @@ export = hello
164
166
  ```
165
167
  Golf will automatically discover this file. The module docstring `"""Hello World tool {{project_name}}."""` is used as the tool's description. It infers parameters from the `hello` function's signature and uses the `Output` Pydantic model for the output schema. The tool will be registered with the ID `hello`.
166
168
 
167
- ## Configuration (`golf.json`)
169
+ ## Authentication & Features
168
170
 
169
- The `golf.json` file is the heart of your Golf project configuration. Here's what each field controls:
171
+ Golf includes enterprise-grade authentication, built-in utilities, and automatic telemetry:
170
172
 
171
- ```jsonc
172
- {
173
- "name": "{{project_name}}", // Your MCP server name (required)
174
- "description": "A Golf project", // Brief description of your server's purpose
175
- "host": "127.0.0.1", // Server host - use "0.0.0.0" to listen on all interfaces
176
- "port": 3000, // Server port - any available port number
177
- "transport": "sse", // Communication protocol:
178
- // - "sse": Server-Sent Events (recommended for web clients)
179
- // - "streamable-http": HTTP with streaming support
180
- // - "stdio": Standard I/O (for CLI integration)
181
-
182
- // HTTP Transport Configuration (optional)
183
- "stateless_http": false, // Make streamable-http transport stateless (new session per request)
184
- // When true, server restarts won't break existing client connections
185
-
186
- // Health Check Configuration (optional)
187
- "health_check_enabled": false, // Enable health check endpoint for Kubernetes/load balancers
188
- "health_check_path": "/health", // HTTP path for health check endpoint
189
- "health_check_response": "OK", // Response text returned by health check
190
-
191
- // OpenTelemetry Configuration (optional)
192
- "opentelemetry_enabled": false, // Enable distributed tracing
193
- "opentelemetry_default_exporter": "console" // Default exporter if OTEL_TRACES_EXPORTER not set
194
- // Options: "console", "otlp_http"
195
- }
196
- ```
197
-
198
- ### Key Configuration Options:
199
-
200
- - **`name`**: The identifier for your MCP server. This will be shown to clients connecting to your server.
201
- - **`transport`**: Choose based on your client needs:
202
- - `"sse"` is ideal for web-based clients and real-time communication
203
- - `"streamable-http"` provides HTTP streaming for traditional API clients
204
- - `"stdio"` enables integration with command-line tools and scripts
205
- - **`host` & `port`**: Control where your server listens. Use `"127.0.0.1"` for local development or `"0.0.0.0"` to accept external connections.
206
- - **`stateless_http`**: When true, makes the streamable-http transport stateless by creating a new session for each request. This ensures that server restarts don't break existing client connections, making the server truly stateless.
207
- - **`health_check_enabled`**: When true, enables a health check endpoint for Kubernetes readiness/liveness probes and load balancers
208
- - **`health_check_path`**: Customizable path for the health check endpoint (defaults to "/health")
209
- - **`health_check_response`**: Customizable response text for successful health checks (defaults to "OK")
210
- - **`opentelemetry_enabled`**: When true, enables distributed tracing for debugging and monitoring your MCP server
211
- - **`opentelemetry_default_exporter`**: Sets the default trace exporter. Can be overridden by the `OTEL_TRACES_EXPORTER` environment variable
212
-
213
- ## Features
214
-
215
- ### 🏥 Health Check Support
216
-
217
- Golf includes built-in health check endpoint support for production deployments. When enabled, it automatically adds a custom HTTP route that can be used by:
218
- - Kubernetes readiness and liveness probes
219
- - Load balancers and reverse proxies
220
- - Monitoring systems
221
- - Container orchestration platforms
222
-
223
- #### Configuration
224
-
225
- Enable health checks in your `golf.json`:
226
- ```json
227
- {
228
- "health_check_enabled": true,
229
- "health_check_path": "/health",
230
- "health_check_response": "Service is healthy"
231
- }
173
+ ```python
174
+ # auth.py - Configure authentication
175
+ from golf.auth import configure_auth, JWTAuthConfig, StaticTokenConfig, OAuthServerConfig
176
+
177
+ # JWT authentication (production)
178
+ configure_auth(JWTAuthConfig(
179
+ jwks_uri_env_var="JWKS_URI",
180
+ issuer_env_var="JWT_ISSUER",
181
+ audience_env_var="JWT_AUDIENCE",
182
+ required_scopes=["read", "write"]
183
+ ))
184
+
185
+ # OAuth Server mode (Golf acts as OAuth 2.0 server)
186
+ # configure_auth(OAuthServerConfig(
187
+ # base_url="https://your-golf-server.com",
188
+ # valid_scopes=["read", "write", "admin"]
189
+ # ))
190
+
191
+ # Static tokens (development only)
192
+ # configure_auth(StaticTokenConfig(
193
+ # tokens={"dev-token": {"client_id": "dev", "scopes": ["read"]}}
194
+ # ))
195
+
196
+ # Built-in utilities available in all tools
197
+ from golf.utils import elicit, sample, get_context
232
198
  ```
233
199
 
234
- The generated server will include a route like:
235
- ```python
236
- @mcp.custom_route('/health', methods=["GET"])
237
- async def health_check(request: Request) -> PlainTextResponse:
238
- """Health check endpoint for Kubernetes and load balancers."""
239
- return PlainTextResponse("Service is healthy")
200
+ ```bash
201
+ # Automatic telemetry with Golf Platform
202
+ export GOLF_API_KEY="your-key"
203
+ golf run # Telemetry enabled automatically
240
204
  ```
241
205
 
242
- ### 🔍 OpenTelemetry Support
206
+ **[📚 Complete Documentation →](https://docs.golf.dev)**
243
207
 
244
- Golf includes built-in OpenTelemetry instrumentation for distributed tracing. When enabled, it automatically traces:
245
- - Tool executions with arguments and results
246
- - Resource reads and template expansions
247
- - Prompt generations
248
- - HTTP requests and sessions
208
+ ## Configuration
249
209
 
250
- #### Configuration
210
+ Basic configuration in `golf.json`:
251
211
 
252
- Enable OpenTelemetry in your `golf.json`:
253
212
  ```json
254
213
  {
255
- "opentelemetry_enabled": true,
256
- "opentelemetry_default_exporter": "otlp_http"
214
+ "name": "My Golf Server",
215
+ "host": "localhost",
216
+ "port": 3000,
217
+ "transport": "sse",
218
+ "opentelemetry_enabled": false,
219
+ "detailed_tracing": false
257
220
  }
258
221
  ```
259
222
 
260
- Then configure via environment variables:
261
- ```bash
262
- # For OTLP HTTP exporter (e.g., Jaeger, Grafana Tempo)
263
- OTEL_TRACES_EXPORTER=otlp_http
264
- OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/traces
265
- OTEL_SERVICE_NAME=my-golf-server # Optional, defaults to project name
266
-
267
- # For console exporter (debugging)
268
- OTEL_TRACES_EXPORTER=console
269
- ```
270
-
271
- **Note**: When using the OTLP HTTP exporter, you must set `OTEL_EXPORTER_OTLP_ENDPOINT`. If not configured, Golf will display a warning and disable tracing to avoid errors.
272
-
273
- ## Roadmap
274
-
275
- Here are the things we are working hard on:
276
-
277
- * **`golf deploy` command for one click deployments to Vercel, Blaxel and other providers**
278
- * **Production-ready OAuth token management, to allow for persistent, encrypted token storage and client mapping**
223
+ - **`transport`**: Choose `"sse"`, `"streamable-http"`, or `"stdio"`
224
+ - **`opentelemetry_enabled`**: Auto-enabled with `GOLF_API_KEY`
225
+ - **`detailed_tracing`**: Capture input/output (use carefully with sensitive data)
279
226
 
280
227
 
281
228
  ## Privacy & Telemetry