Files
intervalsicu-mcp/src/intervals_mcp_server/server.py
T
Chris Farhood 935abf86d4
build-image / build (push) Failing after 18s
Fork intervals-mcp-server: native OAuth + streamable-HTTP, no monkeypatch
- Bump mcp[cli] 1.22 -> 1.28.1 (negotiates MCP protocol 2025-11-25, matching
  current Claude clients; the old 2025-06-18 server never got a tools/list on
  the connector surface).
- Bake transport config into code: stateless_http + json_response for HTTP
  (single JSON body instead of a 34KB SSE stream, which the connector pipeline
  handles far more reliably).
- Bake Authentik OAuth (AuthSettings + JWT TokenVerifier) into intervals_mcp_server.auth,
  configured from MCP_ISSUER/MCP_RESOURCE/MCP_JWKS_URI/MCP_CLIENT_ID — removes the
  runtime FastMCP.__init__ monkeypatch from the k8s deployment command.
- Accept token audience with/without trailing slash (RFC 8707 clients use the
  slash-normalised resource metadata value).
- Dockerfile CMD runs the module (transport via MCP_TRANSPORT); add .gitea CI to
  build+push the image to git.farh.net/farhoodlabs/intervalsicu-mcp.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 15:07:46 -04:00

133 lines
4.5 KiB
Python

"""
Intervals.icu MCP Server
This module implements a Model Context Protocol (MCP) server for connecting
Claude with the Intervals.icu API. It provides tools for retrieving and managing
athlete data, including activities, events, workouts, and wellness metrics.
Main Features:
- Activity retrieval and detailed analysis
- Event management (races, workouts, calendar items)
- Wellness data tracking and visualization
- Error handling with user-friendly messages
- Configurable parameters with environment variable support
Usage:
This server is designed to be run as a standalone script and exposes several MCP tools
for use with Claude Desktop or other MCP-compatible clients. The server loads configuration
from environment variables (optionally via a .env file) and communicates with the Intervals.icu API.
To run the server:
$ python src/intervals_mcp_server/server.py
MCP tools provided:
- get_activities
- get_activity_details
- get_activity_intervals
- get_activity_streams
- get_activity_messages
- add_activity_message
- get_events
- get_event_by_id
- add_or_update_event
- delete_event
- delete_events_by_date_range
- get_wellness_data
- get_athlete_power_curves
- get_custom_items
- get_custom_item_by_id
- create_custom_item
- update_custom_item
- delete_custom_item
See the README for more details on configuration and usage.
"""
import logging
# Import API client and configuration
from intervals_mcp_server.api.client import (
httpx_client, # Re-export for backward compatibility with tests
make_intervals_request,
)
from intervals_mcp_server.config import get_config
from intervals_mcp_server.mcp_instance import mcp
# Import types and validation
from intervals_mcp_server.server_setup import setup_transport, start_server
from intervals_mcp_server.utils.validation import validate_athlete_id
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler()],
)
logger = logging.getLogger("intervals_icu_mcp_server")
# Get configuration instance
config = get_config()
# Import tool modules to register them (tools register themselves via @mcp.tool() decorators)
# Import tool functions for re-export
from intervals_mcp_server.tools.activities import ( # pylint: disable=wrong-import-position # noqa: E402
add_activity_message,
get_activities,
get_activity_details,
get_activity_intervals,
get_activity_messages,
get_activity_streams,
)
from intervals_mcp_server.tools.events import ( # pylint: disable=wrong-import-position # noqa: E402
add_or_update_event,
delete_event,
delete_events_by_date_range,
get_event_by_id,
get_events,
)
from intervals_mcp_server.tools.gear import get_gear_list # pylint: disable=wrong-import-position # noqa: E402
from intervals_mcp_server.tools.wellness import get_wellness_data # pylint: disable=wrong-import-position # noqa: E402
from intervals_mcp_server.tools.power_curves import get_athlete_power_curves # pylint: disable=wrong-import-position # noqa: E402
from intervals_mcp_server.tools.custom_items import ( # pylint: disable=wrong-import-position # noqa: E402
create_custom_item,
delete_custom_item,
get_custom_item_by_id,
get_custom_items,
update_custom_item,
)
# Re-export make_intervals_request and httpx_client for backward compatibility
# pylint: disable=duplicate-code # This __all__ list is intentionally similar to tools/__init__.py
__all__ = [
"make_intervals_request",
"httpx_client", # Re-exported for test compatibility
"add_activity_message",
"get_activities",
"get_activity_details",
"get_activity_intervals",
"get_activity_messages",
"get_activity_streams",
"get_events",
"get_event_by_id",
"delete_event",
"delete_events_by_date_range",
"add_or_update_event",
"get_wellness_data",
"get_athlete_power_curves",
"get_custom_items",
"get_custom_item_by_id",
"create_custom_item",
"update_custom_item",
"delete_custom_item",
]
# Run the server
if __name__ == "__main__":
# Validate ATHLETE_ID when server starts (not at import time to allow tests)
validate_athlete_id(config.athlete_id)
# Setup transport and start server
selected_transport = setup_transport()
start_server(mcp, selected_transport)