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.

@@ -29,6 +29,15 @@ def init_telemetry(service_name: str = "golf-mcp-server") -> TracerProvider | No
29
29
  """
30
30
  global _provider
31
31
 
32
+ # Check for Golf platform integration first
33
+ golf_api_key = os.environ.get("GOLF_API_KEY")
34
+ if golf_api_key and not os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT"):
35
+ # Auto-configure for Golf platform
36
+ os.environ["OTEL_TRACES_EXPORTER"] = "otlp_http"
37
+ os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://localhost:8000/api/v1/otel"
38
+ os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"X-Golf-Key={golf_api_key}"
39
+ print("[INFO] Auto-configured OpenTelemetry for Golf platform ingestion")
40
+
32
41
  # Check for required environment variables based on exporter type
33
42
  exporter_type = os.environ.get("OTEL_TRACES_EXPORTER", "console").lower()
34
43
 
@@ -37,7 +46,8 @@ def init_telemetry(service_name: str = "golf-mcp-server") -> TracerProvider | No
37
46
  endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
38
47
  if not endpoint:
39
48
  print(
40
- "[WARNING] OpenTelemetry tracing is disabled: OTEL_EXPORTER_OTLP_ENDPOINT is not set for OTLP HTTP exporter"
49
+ "[WARNING] OpenTelemetry tracing is disabled: "
50
+ "OTEL_EXPORTER_OTLP_ENDPOINT is not set for OTLP HTTP exporter"
41
51
  )
42
52
  return None
43
53
 
@@ -47,6 +57,14 @@ def init_telemetry(service_name: str = "golf-mcp-server") -> TracerProvider | No
47
57
  "service.version": os.environ.get("SERVICE_VERSION", "1.0.0"),
48
58
  "service.instance.id": os.environ.get("SERVICE_INSTANCE_ID", "default"),
49
59
  }
60
+
61
+ # Add Golf-specific attributes if available
62
+ if golf_api_key:
63
+ golf_server_id = os.environ.get("GOLF_SERVER_ID")
64
+ if golf_server_id:
65
+ resource_attributes["golf.server.id"] = golf_server_id
66
+ resource_attributes["golf.platform.enabled"] = "true"
67
+
50
68
  resource = Resource.create(resource_attributes)
51
69
 
52
70
  # Create provider
@@ -71,6 +89,10 @@ def init_telemetry(service_name: str = "golf-mcp-server") -> TracerProvider | No
71
89
  exporter = OTLPSpanExporter(
72
90
  endpoint=endpoint, headers=header_dict if header_dict else None
73
91
  )
92
+
93
+ # Log successful configuration for Golf platform
94
+ if golf_api_key:
95
+ print(f"[INFO] OpenTelemetry configured for Golf platform: {endpoint}")
74
96
  else:
75
97
  # Default to console exporter
76
98
  exporter = ConsoleSpanExporter(out=sys.stderr)
@@ -113,21 +135,6 @@ def init_telemetry(service_name: str = "golf-mcp-server") -> TracerProvider | No
113
135
  traceback.print_exc()
114
136
  raise
115
137
 
116
- # Create a test span to verify everything is working
117
- try:
118
- test_tracer = provider.get_tracer("golf.telemetry.test", "1.0.0")
119
- with test_tracer.start_as_current_span("startup.test") as span:
120
- span.set_attribute("test", True)
121
- span.set_attribute("service.name", service_name)
122
- span.set_attribute("exporter.type", exporter_type)
123
- span.set_attribute(
124
- "endpoint", os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "not set")
125
- )
126
- except Exception:
127
- import traceback
128
-
129
- traceback.print_exc()
130
-
131
138
  return provider
132
139
 
133
140
 
@@ -154,14 +161,12 @@ def instrument_tool(func: Callable[..., T], tool_name: str) -> Callable[..., T]:
154
161
 
155
162
  tracer = get_tracer()
156
163
 
157
- # Add debug logging
158
- print(
159
- f"[TELEMETRY DEBUG] Instrumenting tool: {tool_name} (function: {func.__name__})"
160
- )
161
-
162
164
  @functools.wraps(func)
163
165
  async def async_wrapper(*args, **kwargs):
164
- print(f"[TELEMETRY DEBUG] Executing async tool: {tool_name}")
166
+ # Record metrics timing
167
+ import time
168
+
169
+ start_time = time.time()
165
170
 
166
171
  # Create a more descriptive span name
167
172
  span_name = f"mcp.tool.{tool_name}.execute"
@@ -205,52 +210,9 @@ def instrument_tool(func: Callable[..., T], tool_name: str) -> Callable[..., T]:
205
210
  if session_id_from_baggage:
206
211
  span.set_attribute("mcp.session.id", session_id_from_baggage)
207
212
 
208
- # Add tool arguments as span attributes (be careful with sensitive data)
209
- for i, arg in enumerate(args):
210
- if isinstance(arg, str | int | float | bool) or arg is None:
211
- span.set_attribute(f"mcp.tool.arg.{i}", str(arg))
212
- elif hasattr(arg, "__dict__"):
213
- # For objects, just record the type
214
- span.set_attribute(f"mcp.tool.arg.{i}.type", type(arg).__name__)
215
-
216
- # Add named arguments with better naming
217
- for key, value in kwargs.items():
218
- if key != "ctx":
219
- if value is None:
220
- span.set_attribute(f"mcp.tool.input.{key}", "null")
221
- elif isinstance(value, str | int | float | bool):
222
- span.set_attribute(f"mcp.tool.input.{key}", str(value))
223
- elif isinstance(value, list | tuple):
224
- span.set_attribute(f"mcp.tool.input.{key}.count", len(value))
225
- span.set_attribute(f"mcp.tool.input.{key}.type", "array")
226
- elif isinstance(value, dict):
227
- span.set_attribute(f"mcp.tool.input.{key}.count", len(value))
228
- span.set_attribute(f"mcp.tool.input.{key}.type", "object")
229
- # Only show first few keys to avoid exceeding attribute limits
230
- if len(value) > 0 and len(value) <= 5:
231
- keys_list = list(value.keys())[:5]
232
- # Limit key length and join
233
- truncated_keys = [
234
- str(k)[:20] + "..." if len(str(k)) > 20 else str(k)
235
- for k in keys_list
236
- ]
237
- span.set_attribute(
238
- f"mcp.tool.input.{key}.sample_keys",
239
- ",".join(truncated_keys),
240
- )
241
- else:
242
- # For other types, at least record the type
243
- span.set_attribute(
244
- f"mcp.tool.input.{key}.type", type(value).__name__
245
- )
246
-
247
213
  # Add event for tool execution start
248
214
  span.add_event("tool.execution.started", {"tool.name": tool_name})
249
215
 
250
- print(
251
- f"[TELEMETRY DEBUG] Tool span created: {span_name} (span_id: {span.get_span_context().span_id:016x})"
252
- )
253
-
254
216
  try:
255
217
  result = await func(*args, **kwargs)
256
218
  span.set_status(Status(StatusCode.OK))
@@ -258,6 +220,19 @@ def instrument_tool(func: Callable[..., T], tool_name: str) -> Callable[..., T]:
258
220
  # Add event for successful completion
259
221
  span.add_event("tool.execution.completed", {"tool.name": tool_name})
260
222
 
223
+ # Record metrics for successful execution
224
+ try:
225
+ from golf.metrics import get_metrics_collector
226
+
227
+ metrics_collector = get_metrics_collector()
228
+ metrics_collector.increment_tool_execution(tool_name, "success")
229
+ metrics_collector.record_tool_duration(
230
+ tool_name, time.time() - start_time
231
+ )
232
+ except ImportError:
233
+ # Metrics not available, continue without metrics
234
+ pass
235
+
261
236
  # Capture result metadata with better structure
262
237
  if result is not None:
263
238
  if isinstance(result, str | int | float | bool):
@@ -288,9 +263,6 @@ def instrument_tool(func: Callable[..., T], tool_name: str) -> Callable[..., T]:
288
263
  # For any result, record its type
289
264
  span.set_attribute("mcp.tool.result.class", type(result).__name__)
290
265
 
291
- print(
292
- f"[TELEMETRY DEBUG] Tool execution completed successfully: {tool_name}"
293
- )
294
266
  return result
295
267
  except Exception as e:
296
268
  span.record_exception(e)
@@ -305,12 +277,26 @@ def instrument_tool(func: Callable[..., T], tool_name: str) -> Callable[..., T]:
305
277
  "error.message": str(e),
306
278
  },
307
279
  )
308
- print(f"[TELEMETRY DEBUG] Tool execution failed: {tool_name} - {e}")
280
+
281
+ # Record metrics for failed execution
282
+ try:
283
+ from golf.metrics import get_metrics_collector
284
+
285
+ metrics_collector = get_metrics_collector()
286
+ metrics_collector.increment_tool_execution(tool_name, "error")
287
+ metrics_collector.increment_error("tool", type(e).__name__)
288
+ except ImportError:
289
+ # Metrics not available, continue without metrics
290
+ pass
291
+
309
292
  raise
310
293
 
311
294
  @functools.wraps(func)
312
295
  def sync_wrapper(*args, **kwargs):
313
- print(f"[TELEMETRY DEBUG] Executing sync tool: {tool_name}")
296
+ # Record metrics timing
297
+ import time
298
+
299
+ start_time = time.time()
314
300
 
315
301
  # Create a more descriptive span name
316
302
  span_name = f"mcp.tool.{tool_name}.execute"
@@ -354,52 +340,9 @@ def instrument_tool(func: Callable[..., T], tool_name: str) -> Callable[..., T]:
354
340
  if session_id_from_baggage:
355
341
  span.set_attribute("mcp.session.id", session_id_from_baggage)
356
342
 
357
- # Add tool arguments as span attributes (be careful with sensitive data)
358
- for i, arg in enumerate(args):
359
- if isinstance(arg, str | int | float | bool) or arg is None:
360
- span.set_attribute(f"mcp.tool.arg.{i}", str(arg))
361
- elif hasattr(arg, "__dict__"):
362
- # For objects, just record the type
363
- span.set_attribute(f"mcp.tool.arg.{i}.type", type(arg).__name__)
364
-
365
- # Add named arguments with better naming
366
- for key, value in kwargs.items():
367
- if key != "ctx":
368
- if value is None:
369
- span.set_attribute(f"mcp.tool.input.{key}", "null")
370
- elif isinstance(value, str | int | float | bool):
371
- span.set_attribute(f"mcp.tool.input.{key}", str(value))
372
- elif isinstance(value, list | tuple):
373
- span.set_attribute(f"mcp.tool.input.{key}.count", len(value))
374
- span.set_attribute(f"mcp.tool.input.{key}.type", "array")
375
- elif isinstance(value, dict):
376
- span.set_attribute(f"mcp.tool.input.{key}.count", len(value))
377
- span.set_attribute(f"mcp.tool.input.{key}.type", "object")
378
- # Only show first few keys to avoid exceeding attribute limits
379
- if len(value) > 0 and len(value) <= 5:
380
- keys_list = list(value.keys())[:5]
381
- # Limit key length and join
382
- truncated_keys = [
383
- str(k)[:20] + "..." if len(str(k)) > 20 else str(k)
384
- for k in keys_list
385
- ]
386
- span.set_attribute(
387
- f"mcp.tool.input.{key}.sample_keys",
388
- ",".join(truncated_keys),
389
- )
390
- else:
391
- # For other types, at least record the type
392
- span.set_attribute(
393
- f"mcp.tool.input.{key}.type", type(value).__name__
394
- )
395
-
396
343
  # Add event for tool execution start
397
344
  span.add_event("tool.execution.started", {"tool.name": tool_name})
398
345
 
399
- print(
400
- f"[TELEMETRY DEBUG] Tool span created: {span_name} (span_id: {span.get_span_context().span_id:016x})"
401
- )
402
-
403
346
  try:
404
347
  result = func(*args, **kwargs)
405
348
  span.set_status(Status(StatusCode.OK))
@@ -407,6 +350,19 @@ def instrument_tool(func: Callable[..., T], tool_name: str) -> Callable[..., T]:
407
350
  # Add event for successful completion
408
351
  span.add_event("tool.execution.completed", {"tool.name": tool_name})
409
352
 
353
+ # Record metrics for successful execution
354
+ try:
355
+ from golf.metrics import get_metrics_collector
356
+
357
+ metrics_collector = get_metrics_collector()
358
+ metrics_collector.increment_tool_execution(tool_name, "success")
359
+ metrics_collector.record_tool_duration(
360
+ tool_name, time.time() - start_time
361
+ )
362
+ except ImportError:
363
+ # Metrics not available, continue without metrics
364
+ pass
365
+
410
366
  # Capture result metadata with better structure
411
367
  if result is not None:
412
368
  if isinstance(result, str | int | float | bool):
@@ -437,9 +393,6 @@ def instrument_tool(func: Callable[..., T], tool_name: str) -> Callable[..., T]:
437
393
  # For any result, record its type
438
394
  span.set_attribute("mcp.tool.result.class", type(result).__name__)
439
395
 
440
- print(
441
- f"[TELEMETRY DEBUG] Tool execution completed successfully: {tool_name}"
442
- )
443
396
  return result
444
397
  except Exception as e:
445
398
  span.record_exception(e)
@@ -454,7 +407,18 @@ def instrument_tool(func: Callable[..., T], tool_name: str) -> Callable[..., T]:
454
407
  "error.message": str(e),
455
408
  },
456
409
  )
457
- print(f"[TELEMETRY DEBUG] Tool execution failed: {tool_name} - {e}")
410
+
411
+ # Record metrics for failed execution
412
+ try:
413
+ from golf.metrics import get_metrics_collector
414
+
415
+ metrics_collector = get_metrics_collector()
416
+ metrics_collector.increment_tool_execution(tool_name, "error")
417
+ metrics_collector.increment_error("tool", type(e).__name__)
418
+ except ImportError:
419
+ # Metrics not available, continue without metrics
420
+ pass
421
+
458
422
  raise
459
423
 
460
424
  # Return appropriate wrapper based on function type
@@ -701,16 +665,6 @@ def instrument_prompt(func: Callable[..., T], prompt_name: str) -> Callable[...,
701
665
  if session_id_from_baggage:
702
666
  span.set_attribute("mcp.session.id", session_id_from_baggage)
703
667
 
704
- # Add prompt arguments
705
- for key, value in kwargs.items():
706
- if key != "ctx":
707
- if isinstance(value, str | int | float | bool) or value is None:
708
- span.set_attribute(f"mcp.prompt.arg.{key}", str(value))
709
- else:
710
- span.set_attribute(
711
- f"mcp.prompt.arg.{key}.type", type(value).__name__
712
- )
713
-
714
668
  # Add event for prompt generation start
715
669
  span.add_event("prompt.generation.started", {"prompt.name": prompt_name})
716
670
 
@@ -805,16 +759,6 @@ def instrument_prompt(func: Callable[..., T], prompt_name: str) -> Callable[...,
805
759
  if session_id_from_baggage:
806
760
  span.set_attribute("mcp.session.id", session_id_from_baggage)
807
761
 
808
- # Add prompt arguments
809
- for key, value in kwargs.items():
810
- if key != "ctx":
811
- if isinstance(value, str | int | float | bool) or value is None:
812
- span.set_attribute(f"mcp.prompt.arg.{key}", str(value))
813
- else:
814
- span.set_attribute(
815
- f"mcp.prompt.arg.{key}.type", type(value).__name__
816
- )
817
-
818
762
  # Add event for prompt generation start
819
763
  span.add_event("prompt.generation.started", {"prompt.name": prompt_name})
820
764
 
@@ -897,13 +841,64 @@ async def telemetry_lifespan(mcp_instance):
897
841
  from starlette.requests import Request
898
842
 
899
843
  class SessionTracingMiddleware(BaseHTTPMiddleware):
844
+ def __init__(self, app):
845
+ super().__init__(app)
846
+ # Track seen sessions to count unique sessions
847
+ self.seen_sessions = set()
848
+ # Track session start times for duration calculation
849
+ self.session_start_times = {}
850
+
900
851
  async def dispatch(self, request: Request, call_next):
852
+ # Record HTTP request timing
853
+ import time
854
+
855
+ start_time = time.time()
856
+
901
857
  # Extract session ID from query params or headers
902
858
  session_id = request.query_params.get("session_id")
903
859
  if not session_id:
904
860
  # Check headers as fallback
905
861
  session_id = request.headers.get("x-session-id")
906
862
 
863
+ # Track session metrics
864
+ if session_id:
865
+ current_time = time.time()
866
+
867
+ # Record new session if we haven't seen this session ID before
868
+ if session_id not in self.seen_sessions:
869
+ self.seen_sessions.add(session_id)
870
+ self.session_start_times[session_id] = current_time
871
+ try:
872
+ from golf.metrics import get_metrics_collector
873
+
874
+ metrics_collector = get_metrics_collector()
875
+ metrics_collector.increment_session()
876
+ except ImportError:
877
+ pass
878
+ else:
879
+ # Update session duration (time since first request)
880
+ if session_id in self.session_start_times:
881
+ duration = (
882
+ current_time - self.session_start_times[session_id]
883
+ )
884
+ try:
885
+ from golf.metrics import get_metrics_collector
886
+
887
+ metrics_collector = get_metrics_collector()
888
+ metrics_collector.record_session_duration(duration)
889
+ except ImportError:
890
+ pass
891
+
892
+ # Clean up old session data periodically
893
+ if len(self.seen_sessions) > 10000:
894
+ # Keep only the most recent 5000 sessions
895
+ recent_sessions = list(self.seen_sessions)[-5000:]
896
+ self.seen_sessions = set(recent_sessions)
897
+ # Clean up start times for removed sessions
898
+ for old_session in list(self.session_start_times.keys()):
899
+ if old_session not in self.seen_sessions:
900
+ self.session_start_times.pop(old_session, None)
901
+
907
902
  # Create a descriptive span name based on the request
908
903
  method = request.method
909
904
  path = request.url.path
@@ -979,6 +974,29 @@ async def telemetry_lifespan(mcp_instance):
979
974
  },
980
975
  )
981
976
 
977
+ # Record HTTP request metrics
978
+ try:
979
+ from golf.metrics import get_metrics_collector
980
+
981
+ metrics_collector = get_metrics_collector()
982
+
983
+ # Clean up path for metrics (remove query params, normalize)
984
+ clean_path = path.split("?")[0] # Remove query parameters
985
+ if clean_path.startswith("/"):
986
+ clean_path = (
987
+ clean_path[1:] or "root"
988
+ ) # Remove leading slash, handle root
989
+
990
+ metrics_collector.increment_http_request(
991
+ method, response.status_code, clean_path
992
+ )
993
+ metrics_collector.record_http_duration(
994
+ method, clean_path, time.time() - start_time
995
+ )
996
+ except ImportError:
997
+ # Metrics not available, continue without metrics
998
+ pass
999
+
982
1000
  return response
983
1001
  except Exception as e:
984
1002
  span.record_exception(e)
@@ -994,6 +1012,25 @@ async def telemetry_lifespan(mcp_instance):
994
1012
  "error.message": str(e),
995
1013
  },
996
1014
  )
1015
+
1016
+ # Record HTTP error metrics
1017
+ try:
1018
+ from golf.metrics import get_metrics_collector
1019
+
1020
+ metrics_collector = get_metrics_collector()
1021
+
1022
+ # Clean up path for metrics
1023
+ clean_path = path.split("?")[0]
1024
+ if clean_path.startswith("/"):
1025
+ clean_path = clean_path[1:] or "root"
1026
+
1027
+ metrics_collector.increment_http_request(
1028
+ method, 500, clean_path
1029
+ ) # Assume 500 for exceptions
1030
+ metrics_collector.increment_error("http", type(e).__name__)
1031
+ except ImportError:
1032
+ pass
1033
+
997
1034
  raise
998
1035
  finally:
999
1036
  if token:
@@ -1004,16 +1041,13 @@ async def telemetry_lifespan(mcp_instance):
1004
1041
  app = getattr(mcp_instance, "app", getattr(mcp_instance, "_app", None))
1005
1042
  if app and hasattr(app, "add_middleware"):
1006
1043
  app.add_middleware(SessionTracingMiddleware)
1007
- print("[TELEMETRY DEBUG] Added SessionTracingMiddleware to FastMCP app")
1008
1044
 
1009
1045
  # Also try to instrument FastMCP's internal handlers
1010
1046
  if hasattr(mcp_instance, "_tool_manager") and hasattr(
1011
1047
  mcp_instance._tool_manager, "tools"
1012
1048
  ):
1013
- print(
1014
- f"[TELEMETRY DEBUG] Found {len(mcp_instance._tool_manager.tools)} tools in FastMCP"
1015
- )
1016
1049
  # The tools should already be instrumented when they were registered
1050
+ pass
1017
1051
 
1018
1052
  # Try to patch FastMCP's request handling to ensure context propagation
1019
1053
  if hasattr(mcp_instance, "handle_request"):
@@ -1026,10 +1060,9 @@ async def telemetry_lifespan(mcp_instance):
1026
1060
  return await original_handle_request(*args, **kwargs)
1027
1061
 
1028
1062
  mcp_instance.handle_request = traced_handle_request
1029
- print("[TELEMETRY DEBUG] Patched FastMCP handle_request method")
1030
1063
 
1031
- except Exception as e:
1032
- print(f"[TELEMETRY DEBUG] Error setting up telemetry middleware: {e}")
1064
+ except Exception:
1065
+ # Silently continue if middleware setup fails
1033
1066
  import traceback
1034
1067
 
1035
1068
  traceback.print_exc()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: golf-mcp
3
- Version: 0.1.16
3
+ Version: 0.1.18
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>=2.0.0
24
+ Requires-Dist: fastmcp<2.6.0,>=2.0.0
25
25
  Requires-Dist: pydantic>=2.11.0
26
26
  Requires-Dist: python-dotenv>=1.1.0
27
27
  Requires-Dist: black>=24.10.0
@@ -34,6 +34,8 @@ Requires-Dist: opentelemetry-sdk>=1.33.1; extra == "telemetry"
34
34
  Requires-Dist: opentelemetry-instrumentation-asgi>=0.40b0; extra == "telemetry"
35
35
  Requires-Dist: opentelemetry-exporter-otlp-proto-http>=0.40b0; extra == "telemetry"
36
36
  Requires-Dist: wrapt>=1.17.0; extra == "telemetry"
37
+ Provides-Extra: metrics
38
+ Requires-Dist: prometheus-client>=0.22.1; extra == "metrics"
37
39
  Dynamic: license-file
38
40
 
39
41
  <div align="center">
@@ -128,7 +130,7 @@ A Golf project initialized with `golf init` will have a structure similar to thi
128
130
 
129
131
  - **`golf.json`**: Configures server name, port, transport, telemetry, and other build settings.
130
132
  - **`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.
131
- - 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 hyphens).
133
+ - 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).
132
134
  - **`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.
133
135
 
134
136
  ## Example: Defining a Tool
@@ -178,6 +180,10 @@ The `golf.json` file is the heart of your Golf project configuration. Here's wha
178
180
  // - "streamable-http": HTTP with streaming support
179
181
  // - "stdio": Standard I/O (for CLI integration)
180
182
 
183
+ // HTTP Transport Configuration (optional)
184
+ "stateless_http": false, // Make streamable-http transport stateless (new session per request)
185
+ // When true, server restarts won't break existing client connections
186
+
181
187
  // Health Check Configuration (optional)
182
188
  "health_check_enabled": false, // Enable health check endpoint for Kubernetes/load balancers
183
189
  "health_check_path": "/health", // HTTP path for health check endpoint
@@ -198,6 +204,7 @@ The `golf.json` file is the heart of your Golf project configuration. Here's wha
198
204
  - `"streamable-http"` provides HTTP streaming for traditional API clients
199
205
  - `"stdio"` enables integration with command-line tools and scripts
200
206
  - **`host` & `port`**: Control where your server listens. Use `"127.0.0.1"` for local development or `"0.0.0.0"` to accept external connections.
207
+ - **`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.
201
208
  - **`health_check_enabled`**: When true, enables a health check endpoint for Kubernetes readiness/liveness probes and load balancers
202
209
  - **`health_check_path`**: Customizable path for the health check endpoint (defaults to "/health")
203
210
  - **`health_check_response`**: Customizable response text for successful health checks (defaults to "OK")
@@ -1,28 +1,30 @@
1
- golf/__init__.py,sha256=yF88-8vL8keLe6gCTumymw0UoMkWkSrJnzLru4zBCLQ,23
1
+ golf/__init__.py,sha256=6BiuMUkhwQp6bzUZSF8np8F1NwCltEtK0sPBF__tepU,23
2
2
  golf/auth/__init__.py,sha256=Rj4yUngJklk6xrDCrxqLTtoDAMzF1HcTvy_l8wREeao,4103
3
3
  golf/auth/api_key.py,sha256=LiIraLiH2v7s3yavidaI6BDlAEfK8XnWF15QmaJn9G4,2378
4
4
  golf/auth/helpers.py,sha256=ZogdcHM7J2PN6cL6F6OLZ3gyoUR3dwAFDxOJQ2DW_bc,6526
5
5
  golf/auth/oauth.py,sha256=-TYcMA4ULWNQacmUvzek2uQVMJpRT3hXC_d5D2k9c44,31156
6
6
  golf/auth/provider.py,sha256=3loeYrkNwIRDvyUkf8gbcCRJSiKiVXgE_rMGCSCr5mk,3802
7
7
  golf/cli/__init__.py,sha256=R8Y8KdD2C8gDo24fXGq-fdWWNeaq3MYjrbaSB8Hb-Hg,45
8
- golf/cli/main.py,sha256=izB2DW4uLtY-twcacMjsBDtywbXaAIhV1EQdgiso2LE,13495
8
+ golf/cli/main.py,sha256=3qexjKNL8vYg-48ATYcwW4-Wv45l3VxntW-mSqDAbEc,13958
9
9
  golf/commands/__init__.py,sha256=GKtIEm7EPQWRgot73RPZPWegwN7Zm0bHtUJbR63FNiw,83
10
10
  golf/commands/build.py,sha256=jhdxB5EwwCC_8PgqdXLUKuBpnycjh0gft3_7EuTo6ro,2319
11
- golf/commands/init.py,sha256=0QoiDt5ERErh2X7l1EozkMgX4voFk4EMY7BDyI7s-vc,8186
11
+ golf/commands/init.py,sha256=DUAvGqOUapWdF2cgWPscqHRvyOZDiajR0F0Wkn_jm-k,10355
12
12
  golf/commands/run.py,sha256=xsiG5LZw4qVt3cRTTfIoWP4Bf4AxNBBJKx0NNfoua40,2884
13
13
  golf/core/__init__.py,sha256=4bKeskJ2fPaZqkz2xQScSa3phRLLrmrczwSL632jv-o,52
14
- golf/core/builder.py,sha256=EUYGGWM7aMGTe5W8ronstrXxA5ej1mURh3RzD2frZ1U,52560
15
- golf/core/builder_auth.py,sha256=6oGw1HsHdtAjfbJKoyakBrFp2v6FgrO1hFuLR9thiY4,13821
14
+ golf/core/builder.py,sha256=flsXnlwOTUhXFMpmZwoxWgnK_oLj4zpSyqcKuDnXezw,59526
15
+ golf/core/builder_auth.py,sha256=nGgyMTiRAqaNfh1FSvoFe6oTVq9RgfMf9JoFGAv2_do,14050
16
+ golf/core/builder_metrics.py,sha256=j6Gtgd867o46JbDfSNGNsHt1QtV1XHKUJs1z8r4siQM,8830
16
17
  golf/core/builder_telemetry.py,sha256=jobFgRSspLQLuitL4ytk6asSUdTqYcDxGY3sTjkrZd4,2654
17
- golf/core/config.py,sha256=GhR9TFzkHbPTPF27-VkoFlI5_anNYyHFW2-dpJeGIzE,7009
18
- golf/core/parser.py,sha256=Yq4mXUwRub4iC7wOUiei8PUnb2TdcnzGHE_BW5PhkT8,21583
19
- golf/core/telemetry.py,sha256=xehArzw3-MVXGA6L1JaxSiX5XIDWDpKfhQUZhX1zjiM,14895
18
+ golf/core/config.py,sha256=6yPtwzVTJauufEnrfUbxsz69H8jC0Ra427oDaRM0-xE,7397
19
+ golf/core/parser.py,sha256=BQRus1O9zmzSmyavwLVfN8BpYFkbrWUDrgQ7yrYfAKw,42457
20
+ golf/core/platform.py,sha256=Z2yEi6ilmQCLC_uAD_oZdVO0WvkL4tsyw7sx0vHhysI,6440
21
+ golf/core/telemetry.py,sha256=CjZ7urbizaRjyFiVBjfGW8V4PmNCG1_quk3FvbVTcjw,15772
20
22
  golf/core/transformer.py,sha256=_0nM42M41oM9zw_XxPVVS6MErdBSw2B5lULC7_UFLfU,5287
21
23
  golf/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
24
  golf/examples/api_key/.env,sha256=15dewTdeJEMAIuzQmh1SFc1zEN6PwryWgAc14IV02lY,90
23
- golf/examples/api_key/.env.example,sha256=zKXwnfCHdmycsESR8z5b8-rvYnaN3SagxrwL6B_VGjA,215
24
- golf/examples/api_key/README.md,sha256=nuc4YIspX_eokoArylBv6rKPv5P8BTq__2YaInidrUk,2892
25
- golf/examples/api_key/golf.json,sha256=y8I43-28eqYW5x2CTdNsb7FaYaDsfcXiUzbyyKF7qto,362
25
+ golf/examples/api_key/.env.example,sha256=fvM_r9xLoiJ_41ZbcDc_EQ48uyxwb7zf0gP_bhQroEY,29
26
+ golf/examples/api_key/README.md,sha256=wRcgwYArwiRcjL6GKEOkPVQal7L37WnREa_rwhrIE10,2892
27
+ golf/examples/api_key/golf.json,sha256=V7atsB5T706bBlkZ5iVBPIhAA6_3Ba3dN6NOyVaV06g,214
26
28
  golf/examples/api_key/pre_build.py,sha256=-12HGLV70sQcPhgN51zx25uN2o48JeFvTByzF2ayYp4,471
27
29
  golf/examples/api_key/tools/issues/create.py,sha256=51X0uGaisUMfMkmJCskArpefpnxZ4fnT4T_f7HhqQdU,2721
28
30
  golf/examples/api_key/tools/issues/list.py,sha256=Egl2o1YVSU8Iigu8XO_iK1-Pn1OnFL3j6LOzaG10rTI,2720
@@ -30,9 +32,9 @@ golf/examples/api_key/tools/repos/list.py,sha256=9JTRLFzA7GwMYAaLjpOD85uXxv0JI4H
30
32
  golf/examples/api_key/tools/search/code.py,sha256=3quEIzrl0i_DM1fjszTcrzaAqMC7p0TZY99NL5GceKY,2993
31
33
  golf/examples/api_key/tools/users/get.py,sha256=VF-hdUkba2_DqKSC_F7vqtbx0EXAK06wUFBEluKqLIA,2470
32
34
  golf/examples/basic/.env,sha256=CqdcvPXopWppurJ3bBjT2dODlKUrLv629BHnOy8zBkM,247
33
- golf/examples/basic/.env.example,sha256=k_wpsqp0oRLLRw520NL3VrbJx4KDZclczYBW1YUgjvw,214
35
+ golf/examples/basic/.env.example,sha256=fqMyaQ7pc9KHFcT4vChBO4AEGrbW7PVXOfqbrCO6j9Q,157
34
36
  golf/examples/basic/README.md,sha256=-mY3R6AAnkXT9FPkALDrJtdf9IyKDvuqjsrLAMTLRYI,2663
35
- golf/examples/basic/golf.json,sha256=wHG_00A4a8IX0nDkZNi08DN0Ax7VHgVtKOOViXgPBu0,314
37
+ golf/examples/basic/golf.json,sha256=8DiRIXmWolrILmbHzEG2tK-ZVwu1W2qBXRLz75XEjAs,166
36
38
  golf/examples/basic/pre_build.py,sha256=AG1N_sKd1UUtHPL7sw1v3YGOcZPQvoa9xcL79S9qjGI,1037
37
39
  golf/examples/basic/prompts/welcome.py,sha256=Qs_OsXdyPNw_cDZU7cnG4a0ZMzka6LF7vmPfax4cmKM,790
38
40
  golf/examples/basic/resources/current_time.py,sha256=hxhV7vGoiOv-DMXVNSVax_jkPoYR3CR9q5PpWYkdliI,1157
@@ -45,11 +47,14 @@ golf/examples/basic/tools/hello.py,sha256=s7Soiq9Wn7oKIvA6Hid8UKB14iyR7HZppIbIT4
45
47
  golf/examples/basic/tools/payments/charge.py,sha256=PIYdFV90hu35H1veLI8ueuYwebzrr5SCTX-x6lxRmU4,1800
46
48
  golf/examples/basic/tools/payments/common.py,sha256=hfyuQRIjrQfSqGjyY55W6pZSD5jL6O0geCE0DGx0v10,1302
47
49
  golf/examples/basic/tools/payments/refund.py,sha256=Qpl4GWvUw-L06oGQMbBzG8pikfCWfBCFcpkRiDOzmyQ,1607
50
+ golf/metrics/__init__.py,sha256=O91y-hj_E9R06gqV8pDZrzHxOIl-1T415Hj9RvFAp3o,283
51
+ golf/metrics/collector.py,sha256=iyRszP8TAAigyOsUFTGdKN8Xeob36LhUvXW9tntJrbA,7617
52
+ golf/metrics/registry.py,sha256=mXQE4Pwf3PopGYjcUu4eGgPDAe085YWcsvcvWk0ny8Q,310
48
53
  golf/telemetry/__init__.py,sha256=ESGCg5HKwTCIfID1e_K7EE0bOWkSzMidlLtdqQgBd0w,396
49
- golf/telemetry/instrumentation.py,sha256=_p26XYLylpH42m7ztJSreqjZRUiXF1GmPPsOtksIQ28,44483
50
- golf_mcp-0.1.16.dist-info/licenses/LICENSE,sha256=5_j2f6fTJmvfmUewzElhkpAaXg2grVoxKouOA8ihV6E,11348
51
- golf_mcp-0.1.16.dist-info/METADATA,sha256=gpn7ea7CCDSDeDQwX3AQD5dEQYRQleMWVaimhrC7wK0,12361
52
- golf_mcp-0.1.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
- golf_mcp-0.1.16.dist-info/entry_points.txt,sha256=5y7rHYM8jGpU-nfwdknCm5XsApLulqsnA37MO6BUTYg,43
54
- golf_mcp-0.1.16.dist-info/top_level.txt,sha256=BQToHcBUufdyhp9ONGMIvPE40jMEtmI20lYaKb4hxOg,5
55
- golf_mcp-0.1.16.dist-info/RECORD,,
54
+ golf/telemetry/instrumentation.py,sha256=Sn7KBJSCf0Kih9ILEdminR0HggpbOlBKToJhnC4PmZE,44934
55
+ golf_mcp-0.1.18.dist-info/licenses/LICENSE,sha256=5_j2f6fTJmvfmUewzElhkpAaXg2grVoxKouOA8ihV6E,11348
56
+ golf_mcp-0.1.18.dist-info/METADATA,sha256=w8GR9q2Ln0UzWTXRFG6XdA1EzbAVUsF7D0tq1nEMqd4,12956
57
+ golf_mcp-0.1.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
58
+ golf_mcp-0.1.18.dist-info/entry_points.txt,sha256=5y7rHYM8jGpU-nfwdknCm5XsApLulqsnA37MO6BUTYg,43
59
+ golf_mcp-0.1.18.dist-info/top_level.txt,sha256=BQToHcBUufdyhp9ONGMIvPE40jMEtmI20lYaKb4hxOg,5
60
+ golf_mcp-0.1.18.dist-info/RECORD,,