Yorrick Jansen Claude commited on
Commit
a0871dc
·
1 Parent(s): 7045b70

Fix port handling and OAuth flow

Browse files

- Remove port searching logic in favor of fixed port 3008
- Fix OAuth flow to properly use client ID from environment variables
- Add validation to ensure client credentials are properly set

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

main.py CHANGED
@@ -1,9 +1,14 @@
 
1
  from strava_mcp.server import mcp
2
 
 
3
 
4
  def main():
5
  """Run the Strava MCP server."""
6
- mcp.run()
 
 
 
7
 
8
 
9
  if __name__ == "__main__":
 
1
+ import logging
2
  from strava_mcp.server import mcp
3
 
4
+ logger = logging.getLogger(__name__)
5
 
6
  def main():
7
  """Run the Strava MCP server."""
8
+ # Use fixed port 3008
9
+ port = 3008
10
+ logger.info(f"Using port {port} for MCP server")
11
+ mcp.run(port=port)
12
 
13
 
14
  if __name__ == "__main__":
strava_mcp/api.py CHANGED
@@ -19,12 +19,11 @@ class StravaAPI:
19
 
20
  Args:
21
  settings: Strava API settings
22
- app: FastAPI app for auth routes (optional)
23
  """
24
  self.settings = settings
25
  self.access_token = None
26
  self.token_expires_at = None
27
- self.app = app
28
  self.auth_flow_in_progress = False
29
  self._client = httpx.AsyncClient(
30
  base_url=settings.base_url,
@@ -36,86 +35,26 @@ class StravaAPI:
36
  await self._client.aclose()
37
 
38
  async def setup_auth_routes(self):
39
- """Set up authentication routes if app is available."""
40
- if not self.app:
41
- logger.warning("No FastAPI app provided, skipping auth routes setup")
42
- return
43
-
44
- from strava_mcp.auth import StravaAuthenticator
45
-
46
- # Check if we have a FastAPI app or a FastMCP server
47
- fastapi_app = None
48
-
49
- # If it's a FastAPI app, use it directly
50
- if hasattr(self.app, "add_api_route"):
51
- fastapi_app = self.app
52
- # If it's a FastMCP server, try to get its underlying app
53
- # FastMCP doesn't have a public API for this, but we can use type checking
54
- # to treat it as a FastAPI instance as it extends FastAPI
55
- else:
56
- # Just use the app itself since it should be a FastAPI instance or subclass
57
- fastapi_app = self.app
58
-
59
- if not fastapi_app:
60
- logger.warning("Could not get FastAPI app from the provided object, auth flow will not be available")
61
- return
62
-
63
- # Create authenticator and set up routes
64
- try:
65
- authenticator = StravaAuthenticator(self.settings.client_id, self.settings.client_secret, fastapi_app)
66
- authenticator.setup_routes(fastapi_app)
67
-
68
- # Store authenticator for later use
69
- self._authenticator = authenticator
70
- logger.info("Successfully set up Strava auth routes")
71
- except Exception as e:
72
- logger.error(f"Error setting up auth routes: {e}")
73
- return
74
 
75
  async def start_auth_flow(self) -> str:
76
- """Start the auth flow to get a refresh token.
 
77
 
78
  Returns:
79
  The refresh token
80
 
81
  Raises:
82
- Exception: If the auth flow fails or is not available
83
  """
84
- if not self.app:
85
- raise Exception(
86
- "No FastAPI app available for auth flow. "
87
- "Please set STRAVA_REFRESH_TOKEN manually in your environment variables."
88
- )
89
-
90
- authenticator = getattr(self, "_authenticator", None)
91
- if not authenticator:
92
- raise Exception(
93
- "Auth routes not set up or setup failed. "
94
- "Please set STRAVA_REFRESH_TOKEN manually in your environment variables."
95
- )
96
-
97
- if self.auth_flow_in_progress:
98
- raise Exception("Auth flow already in progress")
99
-
100
- self.auth_flow_in_progress = True
101
- try:
102
- # Display instructions to the user and open browser
103
- auth_url = self._authenticator.get_authorization_url()
104
- logger.info(
105
- f"\nNo refresh token available. Opening browser for authorization. "
106
- f"If browser doesn't open, please visit this URL manually: {auth_url}"
107
- )
108
-
109
- # Get the refresh token and open browser automatically
110
- refresh_token = await self._authenticator.get_refresh_token(open_browser=True)
111
-
112
- # Store it in settings
113
- self.settings.refresh_token = refresh_token
114
-
115
- logger.info("Successfully obtained refresh token")
116
- return refresh_token
117
- finally:
118
- self.auth_flow_in_progress = False
119
 
120
  async def _ensure_token(self) -> str:
121
  """Ensure we have a valid access token.
@@ -148,21 +87,11 @@ class StravaAPI:
148
  error_msg = f"Failed to get refresh token through OAuth flow: {e}"
149
  logger.error(error_msg)
150
 
151
- # Fall back to the original auth flow if available and requested
152
- if self.app and not self.auth_flow_in_progress:
153
- logger.info("Falling back to MCP-integrated auth flow")
154
- try:
155
- self.settings.refresh_token = await self.start_auth_flow()
156
- except Exception as fallback_error:
157
- raise Exception(
158
- "No refresh token available and all auth flows failed. "
159
- "Please set STRAVA_REFRESH_TOKEN manually in your environment variables."
160
- ) from fallback_error
161
- else:
162
- raise Exception(
163
- "No refresh token available and OAuth flow failed. "
164
- "Please set STRAVA_REFRESH_TOKEN manually in your environment variables."
165
- ) from e
166
 
167
  # Now that we have a refresh token, refresh the access token
168
  async with httpx.AsyncClient() as client:
 
19
 
20
  Args:
21
  settings: Strava API settings
22
+ app: FastAPI app (not used, kept for backward compatibility)
23
  """
24
  self.settings = settings
25
  self.access_token = None
26
  self.token_expires_at = None
 
27
  self.auth_flow_in_progress = False
28
  self._client = httpx.AsyncClient(
29
  base_url=settings.base_url,
 
35
  await self._client.aclose()
36
 
37
  async def setup_auth_routes(self):
38
+ """This method is deprecated and does nothing now.
39
+ Standalone OAuth server is used instead.
40
+ """
41
+ logger.info("Using standalone OAuth server instead of integrated auth routes")
42
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  async def start_auth_flow(self) -> str:
45
+ """This method is deprecated.
46
+ The standalone OAuth server is used instead via _ensure_token().
47
 
48
  Returns:
49
  The refresh token
50
 
51
  Raises:
52
+ Exception: Always raises exception directing to use standalone flow
53
  """
54
+ raise Exception(
55
+ "Integrated auth flow is no longer supported. "
56
+ "The standalone OAuth server will be used automatically when needed."
57
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
  async def _ensure_token(self) -> str:
60
  """Ensure we have a valid access token.
 
87
  error_msg = f"Failed to get refresh token through OAuth flow: {e}"
88
  logger.error(error_msg)
89
 
90
+ # No fallback to MCP-integrated auth flow anymore
91
+ raise Exception(
92
+ "No refresh token available and OAuth flow failed. "
93
+ "Please set STRAVA_REFRESH_TOKEN manually in your environment variables."
94
+ ) from e
 
 
 
 
 
 
 
 
 
 
95
 
96
  # Now that we have a refresh token, refresh the access token
97
  async with httpx.AsyncClient() as client:
strava_mcp/oauth_server.py CHANGED
@@ -126,19 +126,22 @@ class StravaOAuthServer:
126
  if not self.app:
127
  raise ValueError("FastAPI app not initialized")
128
 
129
- config = uvicorn.Config(
130
- app=self.app, # The type checker should now be satisfied
131
- host=self.host,
132
- port=self.port,
133
- log_level="info",
134
- )
135
- self.server = uvicorn.Server(config)
136
  try:
 
 
 
 
 
 
 
 
137
  await self.server.serve()
138
  except Exception as e:
139
  logger.exception("Error running OAuth server")
140
  if not self.token_future.done():
141
  self.token_future.set_exception(e)
 
142
 
143
  async def _stop_server(self):
144
  """Stop the uvicorn server."""
 
126
  if not self.app:
127
  raise ValueError("FastAPI app not initialized")
128
 
129
+ # Use fixed port 3008
 
 
 
 
 
 
130
  try:
131
+ config = uvicorn.Config(
132
+ app=self.app,
133
+ host=self.host,
134
+ port=self.port,
135
+ log_level="info",
136
+ )
137
+
138
+ self.server = uvicorn.Server(config)
139
  await self.server.serve()
140
  except Exception as e:
141
  logger.exception("Error running OAuth server")
142
  if not self.token_future.done():
143
  self.token_future.set_exception(e)
144
+ raise
145
 
146
  async def _stop_server(self):
147
  """Stop the uvicorn server."""
strava_mcp/server.py CHANGED
@@ -29,11 +29,14 @@ async def lifespan(server: FastMCP) -> AsyncIterator[dict[str, Any]]:
29
  """
30
  # Load settings from environment variables
31
  try:
32
- settings = StravaSettings(
33
- client_id="", # Will be overridden by env vars
34
- client_secret="", # Will be overridden by env vars
35
- base_url="https://www.strava.com/api/v3",
36
- )
 
 
 
37
  logger.info("Loaded Strava API settings")
38
  except Exception as e:
39
  logger.error(f"Failed to load Strava API settings: {str(e)}")
 
29
  """
30
  # Load settings from environment variables
31
  try:
32
+ # Let StravaSettings load values directly from env vars
33
+ settings = StravaSettings()
34
+
35
+ if not settings.client_id:
36
+ raise ValueError("STRAVA_CLIENT_ID environment variable is not set")
37
+ if not settings.client_secret:
38
+ raise ValueError("STRAVA_CLIENT_SECRET environment variable is not set")
39
+
40
  logger.info("Loaded Strava API settings")
41
  except Exception as e:
42
  logger.error(f"Failed to load Strava API settings: {str(e)}")
strava_mcp/service.py CHANGED
@@ -23,15 +23,12 @@ class StravaService:
23
  self.api = StravaAPI(settings, app)
24
 
25
  async def initialize(self):
26
- """Initialize the service, setting up auth routes if needed."""
27
- # Set up authentication routes if app is available
28
- await self.api.setup_auth_routes()
29
-
30
- # If we don't have a refresh token, log info about OAuth flow
31
  if not self.settings.refresh_token:
32
  logger.info(
33
  "No STRAVA_REFRESH_TOKEN found in environment. "
34
- "The OAuth flow will be triggered automatically when needed."
35
  )
36
 
37
  async def close(self):
 
23
  self.api = StravaAPI(settings, app)
24
 
25
  async def initialize(self):
26
+ """Initialize the service."""
27
+ # Log info about OAuth flow if no refresh token
 
 
 
28
  if not self.settings.refresh_token:
29
  logger.info(
30
  "No STRAVA_REFRESH_TOKEN found in environment. "
31
+ "The standalone OAuth flow will be triggered automatically when needed."
32
  )
33
 
34
  async def close(self):