Fork intervals-mcp-server: native OAuth + streamable-HTTP, no monkeypatch
build-image / build (push) Failing after 18s

- 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>
This commit is contained in:
2026-07-04 15:07:46 -04:00
commit 935abf86d4
46 changed files with 8485 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
---
description:
globs:
alwaysApply: true
---
# API Communication Patterns
## Intervals.icu API Integration
This project communicates with the Intervals.icu API using consistent patterns defined in [src/intervals_mcp_server/server.py](mdc:src/intervals_mcp_server/server.py).
## Core API Function
All API communication goes through `make_intervals_request()`:
```python
async def make_intervals_request(
url: str,
api_key: str | None = None,
params: dict[str, Any] | None = None
) -> dict[str, Any] | list[dict[str, Any]]
```
### Usage Pattern
- **URL Format**: Relative paths like `/athlete/{id}/activities`
- **Authentication**: HTTP Basic Auth with username "API_KEY" and password as the API key
- **Headers**: Includes User-Agent and Accept: application/json
- **Timeout**: 30 seconds for all requests
## Error Handling Strategy
### HTTP Status Code Mapping
The system provides user-friendly messages for common HTTP errors:
- `401 Unauthorized` - Invalid API key
- `403 Forbidden` - Permission denied
- `404 Not Found` - Resource doesn't exist
- `422 Unprocessable Entity` - Invalid parameters
- `429 Too Many Requests` - Rate limiting
- `500 Internal Server Error` - Server issues
- `503 Service Unavailable` - Maintenance/downtime
### Error Response Format
All errors return a consistent structure:
```python
{
"error": True,
"status_code": int, # HTTP status code
"message": str # User-friendly error message
}
```
## MCP Tool Implementation Pattern
1. **Parameter Validation**: Check required parameters and provide defaults
2. **API Key Resolution**: Use provided key or fall back to global `API_KEY`
3. **Athlete ID Handling**: Support both numeric and i-prefixed formats
4. **Date Validation**: Parse and validate date strings
5. **API Request**: Call `make_intervals_request()` with appropriate parameters
6. **Error Checking**: Return formatted error messages for API failures
7. **Data Formatting**: Use utilities from [src/intervals_mcp_server/utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py)
## Environment Variables
Required configuration (validated on startup):
- `API_KEY` - Cannot be empty
- `ATHLETE_ID` - Must match pattern `r"i?\d+"` (digits or i-prefixed digits)
Optional configuration:
- `INTERVALS_API_BASE_URL` - Defaults to `https://intervals.icu/api/v1`
## Shared HTTP Client
Uses a single `httpx.AsyncClient` instance (`httpx_client`) managed by the FastMCP lifespan context manager to:
- Reuse connections for better performance
- Ensure proper cleanup when server stops
+54
View File
@@ -0,0 +1,54 @@
---
description:
globs:
alwaysApply: true
---
# Development Workflow Guide
## Environment Setup
1. **Use uv for package management**: This project uses [uv](mdc:https:/github.com/astral-sh/uv) instead of pip
```bash
uv venv --python 3.12
source .venv/bin/activate
uv sync --all-extras
```
2. **Environment Configuration**: Copy [.env.example](mdc:.env.example) to `.env` and configure:
- `API_KEY` - Your Intervals.icu API key
- `ATHLETE_ID` - Your athlete ID (digits or i-prefixed)
## Running the Server
- **Manual Testing**: `mcp run src/intervals_mcp_server/server.py`
- **Claude Desktop Integration**: Use `mcp install` command as documented in [README.md](mdc:README.md)
## Code Quality Checks
Before committing, ensure all three checks pass:
1. **Linting**: `ruff .` - Uses default ruff rules, config in [pyproject.toml](mdc:pyproject.toml)
2. **Type Checking**: `mypy src tests` - Static type analysis
3. **Testing**: `pytest` - Unit tests in [tests/](mdc:tests) directory
## Code Organization
- **Main Logic**: All MCP tools are implemented in [src/intervals_mcp_server/server.py](mdc:src/intervals_mcp_server/server.py)
- **Utilities**: Helper functions in [src/intervals_mcp_server/utils/](mdc:src/intervals_mcp_server/utils)
- **API Communication**: `make_intervals_request()` function handles all Intervals.icu API calls
- **Error Handling**: Comprehensive HTTP error handling with user-friendly messages
## Adding New MCP Tools
1. Create async function decorated with `@mcp.tool()`
2. Add proper type hints and docstrings
3. Use `make_intervals_request()` for API calls
4. Add formatting utilities to [src/intervals_mcp_server/utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py) if needed
5. Write unit tests in [tests/](mdc:tests)
## Commit Guidelines
- Use concise commit messages
- Title PRs as `[intervals-mcp-server] <brief description>`
- Ensure `ruff`, `mypy`, and `pytest` all pass
- Document manual testing steps in PR description
+48
View File
@@ -0,0 +1,48 @@
---
description:
globs:
alwaysApply: true
---
# Intervals.icu MCP Server Project Overview
This is a Model Context Protocol (MCP) server for connecting Claude with the Intervals.icu API. The project enables Claude to retrieve and analyze athlete data including activities, events, workouts, and wellness metrics.
## Project Structure
- **Main Entry Point**: [src/intervals_mcp_server/server.py](mdc:src/intervals_mcp_server/server.py) - Contains the FastMCP server implementation with all MCP tools
- **Configuration**: [pyproject.toml](mdc:pyproject.toml) - Project configuration, dependencies, and build settings
- **Environment Setup**: [.env.example](mdc:.env.example) - Template for environment variables (API_KEY, ATHLETE_ID)
- **Documentation**: [README.md](mdc:README.md) - Comprehensive setup and usage guide
- **Developer Guide**: [AGENTS.md](mdc:AGENTS.md) - Contributor and development instructions
## Core Components
### MCP Tools (in server.py)
- `get_activities` - Retrieve athlete activities with filtering options
- `get_activity_details` - Get detailed information for specific activities
- `get_activity_intervals` - Get detailed interval data for activities
- `get_events` - Retrieve upcoming events (workouts, races, etc.)
- `get_event_by_id` - Get detailed information for specific events
- `get_wellness_data` - Fetch wellness metrics and data
### Utilities
- **Formatting**: [src/intervals_mcp_server/utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py) - Data formatting utilities for MCP responses
### Testing
- **Tests Directory**: [tests/](mdc:tests) - Unit tests for server functionality and utilities
- **Sample Data**: [tests/sample_data.py](mdc:tests/sample_data.py) - Test data for development
## Key Technologies
- **Python 3.12+** - Required runtime version
- **FastMCP** - MCP server framework
- **httpx** - Async HTTP client for API calls
- **uv** - Package manager and virtual environment tool
- **pytest** - Testing framework
- **ruff** - Linting and code formatting
- **mypy** - Static type checking
## Environment Variables
- `API_KEY` - Intervals.icu API key (required)
- `ATHLETE_ID` - Target athlete ID (required)
- `INTERVALS_API_BASE_URL` - API base URL (optional, defaults to intervals.icu)
- `LOG_LEVEL` - Logging level (optional, defaults to INFO)
+91
View File
@@ -0,0 +1,91 @@
---
description:
globs:
alwaysApply: true
---
# Python Best Practices for Intervals MCP Server
## Code Style and Readability
- Follow **PEP 8** for formatting, indentation, and naming conventions
- Write readable, maintainable code:
- Use descriptive names for variables, functions, and classes
- Keep functions short and single-purpose (see MCP tools in [server.py](mdc:src/intervals_mcp_server/server.py))
- Keep indentation and spacing consistent
- Embrace Pythonic idioms:
- Use list/dict comprehensions and generators where appropriate
- Prefer built-in functions and stdlib modules
- Use context managers (like the `lifespan` manager for httpx client)
- Avoid global variables except for validated constants (API_KEY, ATHLETE_ID)
- Use lazy **`%`** formatting in logging calls: `logger.debug("val=%s", val)`
- Represent datetimes as timezone-aware UTC objects when possible
## Type Annotations and Documentation
- Add **type hints** to all function signatures (see examples in [server.py](mdc:src/intervals_mcp_server/server.py))
- Use built-in collection types (`list`, `dict`, `set`, `tuple`) instead of `typing.List`, etc.
- Provide clear **docstrings** for:
- All MCP tool functions (required by FastMCP)
- Public utility functions in [utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py)
- The main module docstring explaining the server's purpose
- Use inline comments only for non-obvious logic
## Error Handling and Validation
- Handle errors gracefully with explicit `try/except` blocks (see `make_intervals_request()`)
- Catch specific exceptions: `httpx.HTTPStatusError`, `httpx.RequestError`, etc.
- Return consistent error structures with user-friendly messages
- Validate inputs:
- Check API key and athlete ID on startup
- Validate date formats in MCP tools
- Use regex pattern `r"i?\d+"` for athlete ID validation
## Async Programming Patterns
- Use `async/await` consistently for all MCP tools and API calls
- Share a single `httpx.AsyncClient` instance across requests
- Properly close async resources using lifespan context manager
- Follow FastMCP's async patterns for tool implementations
## Testing Standards
- Write unit tests for all MCP tools and utilities
- Use **pytest** with `pytest-asyncio` for async function testing
- Use **pytest-mock** (`MockerFixture`) for mocking HTTP requests
- Test both success and error paths
- Mock external API calls to avoid dependencies in tests
## Development Environment
- Target **Python 3.12+** as specified in [pyproject.toml](mdc:pyproject.toml)
- Use **uv** for package management: `uv sync --all-extras`
- Manage dependencies in `pyproject.toml` with lock file (`uv.lock`)
- Always use virtual environments (`.venv/`)
- Run quality checks before commits:
- `ruff .` for linting
- `mypy src tests` for type checking
- `pytest` for tests
## Security Practices
- Never hard-code secrets - use environment variables via `.env` file
- Load sensitive data (API_KEY, ATHLETE_ID) from environment
- Use HTTP Basic Auth for API authentication
- Validate all external inputs before processing
- Follow least-privilege principles for API access
## Project-Specific Patterns
- All API communication through `make_intervals_request()` function
- Consistent error response format: `{"error": True, "status_code": int, "message": str}`
- Use formatting utilities from [utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py)
- Follow MCP tool naming conventions: `get_*` for retrieval operations
- Support both numeric and i-prefixed athlete IDs
## Code Organization
- Keep all MCP tools in [server.py](mdc:src/intervals_mcp_server/server.py)
- Place formatting utilities in [utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py)
- Organize tests by functionality in [tests/](mdc:tests) directory
- Use `__init__.py` files to mark Python packages
- Include `py.typed` for type checking support
+89
View File
@@ -0,0 +1,89 @@
---
description:
globs:
alwaysApply: true
---
# Testing Patterns and Practices
## Test Organization
Tests are organized in the [tests/](mdc:tests) directory with the following structure:
- **[tests/test_server.py](mdc:tests/test_server.py)** - Main MCP tool testing
- **[tests/test_formatting.py](mdc:tests/test_formatting.py)** - Utility function tests
- **[tests/test_make_intervals_request.py](mdc:tests/test_make_intervals_request.py)** - API communication tests
- **[tests/sample_data.py](mdc:tests/sample_data.py)** - Mock data for testing
## Testing Framework Setup
- **Framework**: pytest with async support (`pytest-asyncio`)
- **Mocking**: pytest-mock for HTTP request mocking
- **Configuration**: Test settings in [pyproject.toml](mdc:pyproject.toml) under `[tool.pytest.ini_options]`
## Testing Patterns
### Async Testing
All MCP tools are async functions, so tests use:
```python
@pytest.mark.asyncio
async def test_function_name():
# Test async MCP tools
```
### Mock API Responses
HTTP requests are mocked using `pytest-mock`:
```python
def test_api_call(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {"test": "data"}
mock_response.raise_for_status.return_value = None
mocker.patch("httpx.AsyncClient.get", return_value=mock_response)
```
### Test Data Management
- **Sample Data**: [tests/sample_data.py](mdc:tests/sample_data.py) contains realistic mock data
- **Isolation**: Each test uses fresh mock data to avoid side effects
- **Coverage**: Tests cover both success and error scenarios
## Running Tests
```bash
# Run all tests
pytest
# Run with verbose output
pytest -v
# Run specific test file
pytest tests/test_server.py
# Run with coverage (if installed)
pytest --cov=src/intervals_mcp_server
```
## Test Requirements
Before committing code, ensure:
1. **All tests pass**: `pytest` returns exit code 0
2. **No new linting errors**: `ruff .` passes
3. **Type checking passes**: `mypy src tests` succeeds
## Writing New Tests
When adding new MCP tools or utilities:
1. **Create test cases** in appropriate test file
2. **Mock external dependencies** (HTTP requests, file I/O)
3. **Test both success and error paths**
4. **Use realistic test data** from [tests/sample_data.py](mdc:tests/sample_data.py)
5. **Follow async testing patterns** for MCP tools
6. **Verify error message formatting** for user-facing errors
## Mock Strategy
The project uses comprehensive mocking to:
- **Avoid real API calls** during testing
- **Test error handling** by simulating various HTTP error responses
- **Ensure deterministic results** with controlled test data
- **Speed up test execution** by eliminating network requests