golf-mcp 0.1.18__tar.gz → 0.1.19__tar.gz

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 (74) hide show
  1. {golf_mcp-0.1.18/src/golf_mcp.egg-info → golf_mcp-0.1.19}/PKG-INFO +1 -1
  2. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/pyproject.toml +2 -2
  3. golf_mcp-0.1.19/src/golf/__init__.py +1 -0
  4. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/telemetry/instrumentation.py +234 -196
  5. {golf_mcp-0.1.18 → golf_mcp-0.1.19/src/golf_mcp.egg-info}/PKG-INFO +1 -1
  6. golf_mcp-0.1.18/src/golf/__init__.py +0 -1
  7. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/.docs/docs.md +0 -0
  8. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/.docs/fast-mcp.md +0 -0
  9. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/.docs/fastmcp-example-1.py +0 -0
  10. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/.docs/fastmcp-example-2.py +0 -0
  11. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/.docs/mcp.md +0 -0
  12. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/.docs/oauth-implementation.md +0 -0
  13. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/.docs/oauth.md +0 -0
  14. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/LICENSE +0 -0
  15. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/MANIFEST.in +0 -0
  16. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/README.md +0 -0
  17. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/setup.cfg +0 -0
  18. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/auth/__init__.py +0 -0
  19. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/auth/api_key.py +0 -0
  20. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/auth/helpers.py +0 -0
  21. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/auth/oauth.py +0 -0
  22. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/auth/provider.py +0 -0
  23. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/cli/__init__.py +0 -0
  24. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/cli/main.py +0 -0
  25. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/commands/__init__.py +0 -0
  26. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/commands/build.py +0 -0
  27. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/commands/init.py +0 -0
  28. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/commands/run.py +0 -0
  29. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/__init__.py +0 -0
  30. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/builder.py +0 -0
  31. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/builder_auth.py +0 -0
  32. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/builder_metrics.py +0 -0
  33. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/builder_telemetry.py +0 -0
  34. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/config.py +0 -0
  35. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/parser.py +0 -0
  36. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/platform.py +0 -0
  37. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/telemetry.py +0 -0
  38. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/core/transformer.py +0 -0
  39. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/__init__.py +0 -0
  40. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/.env +0 -0
  41. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/.env.example +0 -0
  42. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/README.md +0 -0
  43. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/golf.json +0 -0
  44. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/pre_build.py +0 -0
  45. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/tools/issues/create.py +0 -0
  46. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/tools/issues/list.py +0 -0
  47. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/tools/repos/list.py +0 -0
  48. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/tools/search/code.py +0 -0
  49. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/api_key/tools/users/get.py +0 -0
  50. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/.env +0 -0
  51. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/.env.example +0 -0
  52. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/README.md +0 -0
  53. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/golf.json +0 -0
  54. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/pre_build.py +0 -0
  55. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/prompts/welcome.py +0 -0
  56. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/resources/current_time.py +0 -0
  57. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/resources/info.py +0 -0
  58. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/resources/weather/common.py +0 -0
  59. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/resources/weather/current.py +0 -0
  60. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/resources/weather/forecast.py +0 -0
  61. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/tools/github_user.py +0 -0
  62. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/tools/hello.py +0 -0
  63. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/tools/payments/charge.py +0 -0
  64. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/tools/payments/common.py +0 -0
  65. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/examples/basic/tools/payments/refund.py +0 -0
  66. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/metrics/__init__.py +0 -0
  67. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/metrics/collector.py +0 -0
  68. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/metrics/registry.py +0 -0
  69. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf/telemetry/__init__.py +0 -0
  70. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf_mcp.egg-info/SOURCES.txt +0 -0
  71. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf_mcp.egg-info/dependency_links.txt +0 -0
  72. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf_mcp.egg-info/entry_points.txt +0 -0
  73. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf_mcp.egg-info/requires.txt +0 -0
  74. {golf_mcp-0.1.18 → golf_mcp-0.1.19}/src/golf_mcp.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: golf-mcp
3
- Version: 0.1.18
3
+ Version: 0.1.19
4
4
  Summary: Framework for building MCP servers
5
5
  Author-email: Antoni Gmitruk <antoni@golf.dev>
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "golf-mcp"
7
- version = "0.1.18"
7
+ version = "0.1.19"
8
8
  description = "Framework for building MCP servers"
9
9
  authors = [
10
10
  {name = "Antoni Gmitruk", email = "antoni@golf.dev"}
@@ -67,7 +67,7 @@ golf = ["examples/**/*"]
67
67
 
68
68
  [tool.poetry]
69
69
  name = "golf-mcp"
70
- version = "0.1.18"
70
+ version = "0.1.19"
71
71
  description = "Framework for building MCP servers with zero boilerplate"
72
72
  authors = ["Antoni Gmitruk <antoni@golf.dev>"]
73
73
  license = "Apache-2.0"
@@ -0,0 +1 @@
1
+ __version__ = "0.1.19"
@@ -4,9 +4,11 @@ import asyncio
4
4
  import functools
5
5
  import os
6
6
  import sys
7
+ import time
7
8
  from collections.abc import Callable
8
9
  from contextlib import asynccontextmanager
9
10
  from typing import TypeVar
11
+ from collections import OrderedDict
10
12
 
11
13
  from opentelemetry import baggage, trace
12
14
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
@@ -15,6 +17,9 @@ from opentelemetry.sdk.trace import TracerProvider
15
17
  from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
16
18
  from opentelemetry.trace import Status, StatusCode
17
19
 
20
+ from starlette.middleware.base import BaseHTTPMiddleware
21
+ from starlette.requests import Request
22
+
18
23
  T = TypeVar("T")
19
24
 
20
25
  # Global tracer instance
@@ -821,221 +826,254 @@ def instrument_prompt(func: Callable[..., T], prompt_name: str) -> Callable[...,
821
826
  return sync_wrapper
822
827
 
823
828
 
824
- @asynccontextmanager
825
- async def telemetry_lifespan(mcp_instance):
826
- """Simplified lifespan for telemetry initialization and cleanup."""
827
- global _provider
829
+ # Add the BoundedSessionTracker class before SessionTracingMiddleware
830
+ class BoundedSessionTracker:
831
+ """Memory-safe session tracker with automatic expiration."""
832
+
833
+ def __init__(self, max_sessions: int = 1000, session_ttl: int = 3600):
834
+ self.max_sessions = max_sessions
835
+ self.session_ttl = session_ttl
836
+ self.sessions: OrderedDict[str, float] = OrderedDict()
837
+ self.last_cleanup = time.time()
838
+
839
+ def track_session(self, session_id: str) -> bool:
840
+ """Track a session, returns True if it's new."""
841
+ current_time = time.time()
842
+
843
+ # Periodic cleanup (every 5 minutes)
844
+ if current_time - self.last_cleanup > 300:
845
+ self._cleanup_expired(current_time)
846
+ self.last_cleanup = current_time
847
+
848
+ # Check if session exists and is still valid
849
+ if session_id in self.sessions:
850
+ # Move to end (mark as recently used)
851
+ self.sessions.move_to_end(session_id)
852
+ return False
853
+
854
+ # New session
855
+ self.sessions[session_id] = current_time
856
+
857
+ # Enforce max size
858
+ while len(self.sessions) > self.max_sessions:
859
+ self.sessions.popitem(last=False) # Remove oldest
860
+
861
+ return True
862
+
863
+ def _cleanup_expired(self, current_time: float):
864
+ """Remove expired sessions."""
865
+ expired = [
866
+ sid
867
+ for sid, timestamp in self.sessions.items()
868
+ if current_time - timestamp > self.session_ttl
869
+ ]
870
+ for sid in expired:
871
+ del self.sessions[sid]
872
+
873
+ def get_active_session_count(self) -> int:
874
+ return len(self.sessions)
875
+
876
+
877
+ class SessionTracingMiddleware(BaseHTTPMiddleware):
878
+ def __init__(self, app):
879
+ super().__init__(app)
880
+ # Use memory-safe session tracker instead of unbounded collections
881
+ self.session_tracker = BoundedSessionTracker(
882
+ max_sessions=1000, session_ttl=3600
883
+ )
828
884
 
829
- # Initialize telemetry with the server name
830
- provider = init_telemetry(service_name=mcp_instance.name)
885
+ async def dispatch(self, request: Request, call_next):
886
+ # Record HTTP request timing
887
+ import time
831
888
 
832
- # If provider is None, telemetry is disabled
833
- if provider is None:
834
- # Just yield without any telemetry setup
835
- yield
836
- return
889
+ start_time = time.time()
837
890
 
838
- # Try to add session tracking middleware if possible
839
- try:
840
- from starlette.middleware.base import BaseHTTPMiddleware
841
- from starlette.requests import Request
842
-
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
-
851
- async def dispatch(self, request: Request, call_next):
852
- # Record HTTP request timing
853
- import time
854
-
855
- start_time = time.time()
856
-
857
- # Extract session ID from query params or headers
858
- session_id = request.query_params.get("session_id")
859
- if not session_id:
860
- # Check headers as fallback
861
- session_id = request.headers.get("x-session-id")
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
-
902
- # Create a descriptive span name based on the request
903
- method = request.method
904
- path = request.url.path
905
-
906
- # Determine the operation type from the path
907
- operation_type = "unknown"
908
- if "/mcp" in path:
909
- operation_type = "mcp.request"
910
- elif "/sse" in path:
911
- operation_type = "sse.stream"
912
- elif "/auth" in path:
913
- operation_type = "auth"
914
-
915
- span_name = f"{operation_type}.{method.lower()}"
891
+ # Extract session ID from query params or headers
892
+ session_id = request.query_params.get("session_id")
893
+ if not session_id:
894
+ # Check headers as fallback
895
+ session_id = request.headers.get("x-session-id")
916
896
 
917
- tracer = get_tracer()
918
- with tracer.start_as_current_span(span_name) as span:
919
- # Add comprehensive HTTP attributes
920
- span.set_attribute("http.method", method)
921
- span.set_attribute("http.url", str(request.url))
922
- span.set_attribute("http.scheme", request.url.scheme)
923
- span.set_attribute("http.host", request.url.hostname or "unknown")
924
- span.set_attribute("http.target", path)
925
- span.set_attribute(
926
- "http.user_agent", request.headers.get("user-agent", "unknown")
927
- )
897
+ # Track session metrics using memory-safe tracker
898
+ if session_id:
899
+ is_new_session = self.session_tracker.track_session(session_id)
928
900
 
929
- # Add session tracking
930
- if session_id:
931
- span.set_attribute("mcp.session.id", session_id)
932
- # Add to baggage for propagation
933
- ctx = baggage.set_baggage("mcp.session.id", session_id)
934
- from opentelemetry import context
935
-
936
- token = context.attach(ctx)
937
- else:
938
- token = None
939
-
940
- # Add request size if available
941
- content_length = request.headers.get("content-length")
942
- if content_length:
943
- span.set_attribute("http.request.size", int(content_length))
944
-
945
- # Add event for request start
946
- span.add_event(
947
- "http.request.started", {"method": method, "path": path}
901
+ if is_new_session:
902
+ try:
903
+ from golf.metrics import get_metrics_collector
904
+
905
+ metrics_collector = get_metrics_collector()
906
+ metrics_collector.increment_session()
907
+ except ImportError:
908
+ pass
909
+ else:
910
+ # Record session duration for existing sessions
911
+ try:
912
+ from golf.metrics import get_metrics_collector
913
+
914
+ metrics_collector = get_metrics_collector()
915
+ # Use a default duration since we don't track exact start times anymore
916
+ # This is less precise but memory-safe
917
+ metrics_collector.record_session_duration(300.0) # 5 min default
918
+ except ImportError:
919
+ pass
920
+
921
+ # Create a descriptive span name based on the request
922
+ method = request.method
923
+ path = request.url.path
924
+
925
+ # Determine the operation type from the path
926
+ operation_type = "unknown"
927
+ if "/mcp" in path:
928
+ operation_type = "mcp.request"
929
+ elif "/sse" in path:
930
+ operation_type = "sse.stream"
931
+ elif "/auth" in path:
932
+ operation_type = "auth"
933
+
934
+ span_name = f"{operation_type}.{method.lower()}"
935
+
936
+ tracer = get_tracer()
937
+ with tracer.start_as_current_span(span_name) as span:
938
+ # Add comprehensive HTTP attributes
939
+ span.set_attribute("http.method", method)
940
+ span.set_attribute("http.url", str(request.url))
941
+ span.set_attribute("http.scheme", request.url.scheme)
942
+ span.set_attribute("http.host", request.url.hostname or "unknown")
943
+ span.set_attribute("http.target", path)
944
+ span.set_attribute(
945
+ "http.user_agent", request.headers.get("user-agent", "unknown")
946
+ )
947
+
948
+ # Add session tracking
949
+ if session_id:
950
+ span.set_attribute("mcp.session.id", session_id)
951
+ span.set_attribute(
952
+ "mcp.session.active_count",
953
+ self.session_tracker.get_active_session_count(),
954
+ )
955
+ # Add to baggage for propagation
956
+ ctx = baggage.set_baggage("mcp.session.id", session_id)
957
+ from opentelemetry import context
958
+
959
+ token = context.attach(ctx)
960
+ else:
961
+ token = None
962
+
963
+ # Add request size if available
964
+ content_length = request.headers.get("content-length")
965
+ if content_length:
966
+ span.set_attribute("http.request.size", int(content_length))
967
+
968
+ # Add event for request start
969
+ span.add_event("http.request.started", {"method": method, "path": path})
970
+
971
+ try:
972
+ response = await call_next(request)
973
+
974
+ # Add response attributes
975
+ span.set_attribute("http.status_code", response.status_code)
976
+ span.set_attribute(
977
+ "http.status_class", f"{response.status_code // 100}xx"
978
+ )
979
+
980
+ # Set span status based on HTTP status
981
+ if response.status_code >= 400:
982
+ span.set_status(
983
+ Status(StatusCode.ERROR, f"HTTP {response.status_code}")
948
984
  )
985
+ else:
986
+ span.set_status(Status(StatusCode.OK))
987
+
988
+ # Add event for request completion
989
+ span.add_event(
990
+ "http.request.completed",
991
+ {
992
+ "method": method,
993
+ "path": path,
994
+ "status_code": response.status_code,
995
+ },
996
+ )
949
997
 
950
- try:
951
- response = await call_next(request)
998
+ # Record HTTP request metrics
999
+ try:
1000
+ from golf.metrics import get_metrics_collector
952
1001
 
953
- # Add response attributes
954
- span.set_attribute("http.status_code", response.status_code)
955
- span.set_attribute(
956
- "http.status_class", f"{response.status_code // 100}xx"
957
- )
1002
+ metrics_collector = get_metrics_collector()
958
1003
 
959
- # Set span status based on HTTP status
960
- if response.status_code >= 400:
961
- span.set_status(
962
- Status(StatusCode.ERROR, f"HTTP {response.status_code}")
963
- )
964
- else:
965
- span.set_status(Status(StatusCode.OK))
966
-
967
- # Add event for request completion
968
- span.add_event(
969
- "http.request.completed",
970
- {
971
- "method": method,
972
- "path": path,
973
- "status_code": response.status_code,
974
- },
975
- )
1004
+ # Clean up path for metrics (remove query params, normalize)
1005
+ clean_path = path.split("?")[0] # Remove query parameters
1006
+ if clean_path.startswith("/"):
1007
+ clean_path = (
1008
+ clean_path[1:] or "root"
1009
+ ) # Remove leading slash, handle root
976
1010
 
977
- # Record HTTP request metrics
978
- try:
979
- from golf.metrics import get_metrics_collector
1011
+ metrics_collector.increment_http_request(
1012
+ method, response.status_code, clean_path
1013
+ )
1014
+ metrics_collector.record_http_duration(
1015
+ method, clean_path, time.time() - start_time
1016
+ )
1017
+ except ImportError:
1018
+ # Metrics not available, continue without metrics
1019
+ pass
980
1020
 
981
- metrics_collector = get_metrics_collector()
1021
+ return response
1022
+ except Exception as e:
1023
+ span.record_exception(e)
1024
+ span.set_status(Status(StatusCode.ERROR, str(e)))
982
1025
 
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
1026
+ # Add event for error
1027
+ span.add_event(
1028
+ "http.request.error",
1029
+ {
1030
+ "method": method,
1031
+ "path": path,
1032
+ "error.type": type(e).__name__,
1033
+ "error.message": str(e),
1034
+ },
1035
+ )
989
1036
 
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
-
1000
- return response
1001
- except Exception as e:
1002
- span.record_exception(e)
1003
- span.set_status(Status(StatusCode.ERROR, str(e)))
1004
-
1005
- # Add event for error
1006
- span.add_event(
1007
- "http.request.error",
1008
- {
1009
- "method": method,
1010
- "path": path,
1011
- "error.type": type(e).__name__,
1012
- "error.message": str(e),
1013
- },
1014
- )
1037
+ # Record HTTP error metrics
1038
+ try:
1039
+ from golf.metrics import get_metrics_collector
1015
1040
 
1016
- # Record HTTP error metrics
1017
- try:
1018
- from golf.metrics import get_metrics_collector
1041
+ metrics_collector = get_metrics_collector()
1019
1042
 
1020
- metrics_collector = get_metrics_collector()
1043
+ # Clean up path for metrics
1044
+ clean_path = path.split("?")[0]
1045
+ if clean_path.startswith("/"):
1046
+ clean_path = clean_path[1:] or "root"
1021
1047
 
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"
1048
+ metrics_collector.increment_http_request(
1049
+ method, 500, clean_path
1050
+ ) # Assume 500 for exceptions
1051
+ metrics_collector.increment_error("http", type(e).__name__)
1052
+ except ImportError:
1053
+ pass
1026
1054
 
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
1055
+ raise
1056
+ finally:
1057
+ if token:
1058
+ context.detach(token)
1033
1059
 
1034
- raise
1035
- finally:
1036
- if token:
1037
- context.detach(token)
1038
1060
 
1061
+ @asynccontextmanager
1062
+ async def telemetry_lifespan(mcp_instance):
1063
+ """Simplified lifespan for telemetry initialization and cleanup."""
1064
+ global _provider
1065
+
1066
+ # Initialize telemetry with the server name
1067
+ provider = init_telemetry(service_name=mcp_instance.name)
1068
+
1069
+ # If provider is None, telemetry is disabled
1070
+ if provider is None:
1071
+ # Just yield without any telemetry setup
1072
+ yield
1073
+ return
1074
+
1075
+ # Try to add session tracking middleware if possible
1076
+ try:
1039
1077
  # Try to add middleware to FastMCP app if it has Starlette app
1040
1078
  if hasattr(mcp_instance, "app") or hasattr(mcp_instance, "_app"):
1041
1079
  app = getattr(mcp_instance, "app", getattr(mcp_instance, "_app", None))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: golf-mcp
3
- Version: 0.1.18
3
+ Version: 0.1.19
4
4
  Summary: Framework for building MCP servers
5
5
  Author-email: Antoni Gmitruk <antoni@golf.dev>
6
6
  License-Expression: Apache-2.0
@@ -1 +0,0 @@
1
- __version__ = "0.1.18"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes