Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,19 @@ STRAVA_REFRESH_TOKEN=your_refresh_token_here
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
AUTH_TOKEN=your_secure_random_token_here

# OAuth configuration (Claude Web/Mobile)
OAUTH_ENABLED=false
OAUTH_CLIENTS_TABLE=strava-mcp-oauth-clients
OAUTH_CODES_TABLE=strava-mcp-oauth-codes
OAUTH_TOKENS_TABLE=strava-mcp-oauth-tokens
OAUTH_ACCESS_TOKEN_TTL_SECONDS=3600
OAUTH_REFRESH_TOKEN_TTL_SECONDS=2592000
OAUTH_ALLOWED_REDIRECT_URIS=https://claude.ai/api/mcp/auth_callback,https://claude.com/api/mcp/auth_callback
# Optional: require a bearer token for dynamic client registration
# OAUTH_REGISTRATION_TOKEN=your-registration-token

# Optional: pull Strava credentials from AWS Secrets Manager JSON payload
# SECRETS_MANAGER_ARN=arn:aws:secretsmanager:us-east-1:123456789012:secret:your-secret-id

# Server configuration (optional, defaults to 3000)
PORT=3000
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
```
StravaMCP/
├── src/
│ ├── lambda.ts # Lambda entry point (JSON-RPC over HTTP)
│ ├── lambda-web.ts # Lambda entry point (JSON-RPC over HTTP)
│ ├── index.ts # Local dev server (JSON-RPC over HTTP)
│ ├── lib/
│ │ └── strava-client.ts # OAuth client with auto-refresh
Expand Down Expand Up @@ -78,7 +78,7 @@ StravaMCP/

## Key Components

### 1. Lambda Handler (src/lambda.ts)
### 1. Lambda Handler (src/lambda-web.ts)

**Purpose**: Serverless entry point with remote MCP support

Expand Down Expand Up @@ -266,7 +266,7 @@ export const myNewTools = [
];
```

**Step 2**: Register in `src/lambda.ts` and `src/index.ts`:
**Step 2**: Register in `src/lambda-web.ts` and `src/index.ts`:
```typescript
import { myNewTools } from './tools/my-new-tool.js';

Expand Down Expand Up @@ -452,7 +452,7 @@ Lambda functions "sleep" after inactivity:
### Fix Authentication Issue

1. Check `samconfig.toml` has AUTH_TOKEN
2. Verify middleware in `lambda.ts` validates correctly
2. Verify middleware in `lambda-web.ts` validates correctly
3. Test: `curl -H "Authorization: Bearer <token>" <url>/health`
4. Check CloudWatch logs: `sam logs -n StravaMCPFunction --stack-name strava-mcp-stack --tail`

Expand Down Expand Up @@ -561,7 +561,7 @@ sam delete --stack-name strava-mcp-stack # Delete deployment

### File Locations

- Lambda handler: `src/lambda.ts`
- Lambda handler: `src/lambda-web.ts`
- Local dev server: `src/index.ts`
- Tools: `src/tools/*.ts`
- StravaClient: `src/lib/strava-client.ts`
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ export const myTools = [

### 2. Register Tool

Update `src/lambda.ts` and `src/index.ts`:
Update `src/lambda-web.ts` and `src/index.ts`:

```typescript
import { myTools } from './tools/my-tool.js';
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Perfect for portfolios - demonstrates cloud architecture, serverless deployment,
- 🔐 **Bearer Token Authentication** - Secure access to your data (100% free)
- 🔄 **Automatic OAuth Token Refresh** - Set it and forget it
- ☁️ **AWS Lambda Deployment** - $0/month on free tier
- 📱 **Claude Web & Mobile Support** - Use anywhere
- 📱 **Claude Web & Mobile OAuth** - Secure connector support
- 🤖 **ChatGPT Compatible** - Works with OpenAI's ChatGPT connectors
- 🏃 **13 Strava API Tools** - Complete API coverage (11 Strava-specific + 2 OpenAI-required)
- 🔍 **Smart Search** - Natural language activity search for ChatGPT
Expand Down Expand Up @@ -125,9 +125,11 @@ The deployment automatically shows your complete configuration!

Simply **copy the JSON configuration** displayed after deployment and paste it into:

**Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json`
**Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json`
Use the `/mcp` endpoint and Bearer token authentication.

**Claude Web/Mobile**: Settings → MCP Servers (add URL and Authorization header)
**Claude Web/Mobile (OAuth)**: Settings → Connectors → Add custom connector
Use the **base URL only** (no `/mcp` or `/sse`), and Claude will complete the OAuth flow.

Need to see the config again? Run:
```bash
Expand Down Expand Up @@ -274,7 +276,7 @@ Traditional MCP servers can't be used with Claude web/mobile because they run lo
```
/StravaMCP
├── src/
│ ├── lambda.ts # Lambda handler (Streamable HTTP)
│ ├── lambda-web.ts # Lambda handler (Streamable HTTP)
│ ├── index.ts # Express server (local dev)
│ ├── lib/ # Strava client with OAuth
│ ├── tools/ # MCP tool definitions
Expand Down
61 changes: 20 additions & 41 deletions docs/authentication.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# Authentication Guide

This guide explains how to secure your Strava MCP server with Bearer token authentication.
This guide explains how to secure your Strava MCP server with OAuth (Claude Web/Mobile) and Bearer token authentication (desktop clients).

## Overview

The Lambda function uses **Bearer token authentication** to secure access to your Strava data. This provides:
The Lambda function supports two authentication modes:

1. **OAuth 2.1 (Claude Web/Mobile)** via Dynamic Client Registration and the authorization code flow.
2. **Bearer token authentication** for Claude Desktop and other MCP clients that support custom headers.

Bearer token authentication provides:

- ✅ Simple, effective security without complex AWS services
- ✅ 100% free (no additional costs)
- ✅ Works with Claude web, mobile, and desktop
- ✅ Works with Claude Desktop and other MCP clients that support headers
- ✅ Easy to rotate tokens when needed

## Authentication Modes
Expand Down Expand Up @@ -37,32 +42,13 @@ For Claude Desktop and other MCP clients that support custom headers.
└──────────────┘
```

### 2. Authless Mode (For Claude.ai Custom Connectors)
### OAuth (Claude Web/Mobile)

Claude.ai custom connectors only support authless or OAuth 2.1 (DCR) connections. Since OAuth 2.1 with DCR is complex to implement, this server provides an **authless mode** for SSE endpoints.
Claude Web connects using OAuth:

When `ALLOW_AUTHLESS=true` (default), the SSE transport endpoints (`/sse`, `/sse/`, `/message`) bypass authentication, allowing Claude.ai to connect without Bearer tokens.

```
┌──────────────┐
│ Claude.ai │
│ (Web/Mobile) │
└──────┬───────┘
│ SSE connection (no auth required)
▼
┌──────────────┐
│ Lambda │─── ALLOW_AUTHLESS=true
│ (Middleware)│ Skips auth for SSE
└──────┬───────┘
│ Direct access
▼
┌──────────────┐
│ MCP Server │
│ (Strava API) │
└──────────────┘
```

**Important**: The `/mcp` JSON-RPC endpoint still requires Bearer token authentication for backward compatibility with other clients.
1. Metadata discovery at `/.well-known/oauth-authorization-server`
2. Dynamic client registration at `/register`
3. Authorization code + PKCE flow via `/authorize` and `/token`

## Setup Steps

Expand All @@ -87,8 +73,8 @@ bun run deploy:show-config

This displays:
- Complete Claude Desktop JSON configuration
- Claude Web/Mobile connection details
- Your AUTH_TOKEN
- Claude Web/Mobile OAuth base URL
- Your AUTH_TOKEN (for desktop clients)
- Health check test command

### 2. Deploy with Authentication
Expand Down Expand Up @@ -133,7 +119,7 @@ parameter_overrides = [

After deployment, get your Function URL from the deployment output.

#### All Claude Platforms
#### Claude Desktop (Bearer Token)

Add as a Custom Connector in Claude Settings:

Expand All @@ -149,19 +135,12 @@ Add as a Custom Connector in Claude Settings:

**Important**: The URL must end with `/mcp` - this is the MCP endpoint.

#### Claude.ai Custom Connectors (Authless Mode)

For Claude.ai web and mobile, use the authless SSE transport:
#### Claude Web/Mobile (OAuth)

1. Go to **Claude.ai** → **Settings** → **Connectors** → **Add custom connector**
2. Enter just the **base URL** (no `/mcp` or `/sse` path):
```
https://your-function-url.lambda-url.us-east-1.on.aws
```
3. Claude.ai will automatically discover the `/sse` endpoint
4. Verify tools appear in the conversation
Settings → Connectors → Add custom connector:

**Note**: Authless mode is enabled by default (`ALLOW_AUTHLESS=true`). Claude.ai cannot send custom headers, so Bearer token auth is not supported for Claude.ai connectors.
- URL: `https://your-function-url.lambda-url.us-east-1.on.aws` (base URL only)
- Claude will complete OAuth automatically

### 4. Test Authentication

Expand Down
26 changes: 15 additions & 11 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ CloudFormation outputs from deployed stack
Outputs
---------------------------------------------------------
Key ClaudeConnectionUrl
Description URL to use in Claude web/mobile for MCP connection
Value https://abc123xyz.lambda-url.us-east-1.on.aws/mcp
Description Base URL for Claude OAuth connector
Value https://abc123xyz.lambda-url.us-east-1.on.aws

Key HealthCheckUrl
Description Health check endpoint
Expand All @@ -175,10 +175,9 @@ bun run deploy:show-config
```

This displays:
- Complete Claude Desktop JSON configuration (ready to copy-paste)
- Claude Web/Mobile connection details
- Your AUTH_TOKEN (for authentication)
- Test command with your credentials
- Base URL for Claude OAuth connector
- OAuth metadata endpoint
- MCP endpoint for desktop clients

{: .tip }
> Use this command whenever you need to reconnect Claude or check your deployment details!
Expand All @@ -196,12 +195,20 @@ Expected response:

## Connecting to Claude

### All Claude Platforms (Web, Desktop, Mobile)
### Claude Web/Mobile (OAuth)

1. Open Claude Settings
2. Navigate to **Connectors**
3. Click **Add custom connector**
4. Enter the **base URL** from deployment (no `/mcp` or `/sse`)
5. Claude will complete OAuth automatically

### Claude Desktop (Bearer Token)

1. Open Claude Settings
2. Navigate to **Custom Connectors** (or **MCP Servers**)
3. Click **Add Connector**
4. Enter the configuration from deployment:
4. Enter:
```json
{
"name": "strava",
Expand All @@ -213,9 +220,6 @@ Expected response:
```
5. Save

{: .tip }
> The URL must end with `/mcp` - this is the MCP endpoint that handles all requests.

{: .tip }
> You can now use Strava MCP tools in any Claude conversation!

Expand Down
8 changes: 4 additions & 4 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ graph TB
end

subgraph "AWS Lambda"
D[Lambda Handler<br/>lambda.ts]
D[Lambda Handler<br/>lambda-web.ts]
E[Bearer Token<br/>Middleware]
F[MCP Server<br/>@modelcontextprotocol/sdk]
end
Expand Down Expand Up @@ -170,7 +170,7 @@ graph LR
```
StravaMCP/
├── src/
│ ├── lambda.ts # Lambda entry point (Streamable HTTP)
│ ├── lambda-web.ts # Lambda entry point (Streamable HTTP)
│ ├── index.ts # Local dev server (JSON-RPC over HTTP)
│ ├── lib/
│ │ └── strava-client.ts # OAuth client with auto-refresh
Expand Down Expand Up @@ -209,7 +209,7 @@ StravaMCP/

## Key Components

### 1. Lambda Handler (`src/lambda.ts`)
### 1. Lambda Handler (`src/lambda-web.ts`)

The Lambda entry point implements:
- **Streamable HTTP Transport** for remote MCP
Expand Down Expand Up @@ -359,7 +359,7 @@ export const myNewTools = [

### Step 2: Register Tool

Add to `src/lambda.ts` (Lambda) and `src/index.ts` (local dev):
Add to `src/lambda-web.ts` (Lambda) and `src/index.ts` (local dev):

```typescript
import { myNewTools } from './tools/my-new-tool.js';
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The **Strava MCP Server** is a production-ready Model Context Protocol server th

- 🔐 **Automatic OAuth Token Refresh** - Set it and forget it
- ☁️ **Serverless AWS Lambda** - Runs in the free tier
- 📱 **Works with Claude Web & Mobile** - Use MCP anywhere
- 📱 **Claude Web & Mobile OAuth** - Secure connector support
- 🏃 **11 Strava API Tools** - Activities, athlete stats, streams, clubs, uploads
- 🎯 **Activity Enrichment** - Transform generic workout titles into detailed training logs
- ⚡ **Built with Bun** - Fast builds and deployments
Expand Down Expand Up @@ -64,7 +64,7 @@ Follow the prompts to enter your Strava credentials and AWS region.

After deployment, copy the `ClaudeConnectionUrl` and add it to Claude:

**Claude Web**: Settings → MCP → Add Remote Server
**Claude Web**: Settings → Connectors → Add custom connector (OAuth, base URL)
**Claude Mobile**: Settings → MCP Servers → Add Server

## Architecture
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
"build": "bun run typecheck && bun run build:tsc",
"build:tsc": "tsc",
"build:lambda": "bun run build && bun run copy-files",
"copy-files": "cp package.json dist/ && cp .env.example dist/",
"copy-files": "cp package.json dist/ && cp .env.example dist/ && cp run.sh dist/",
"dev": "bun --watch src/index.ts",
"dev:lambda": "bun src/lambda.ts",
"dev:lambda": "bun src/lambda-web.ts",
"start": "bun dist/index.js",
"typecheck": "tsc --noEmit",
"deploy": "bun run build:lambda && bun scripts/deploy.ts",
Expand All @@ -31,7 +31,9 @@
"author": "",
"license": "ISC",
"dependencies": {
"@codegenie/serverless-express": "^4.15.0",
"@aws-sdk/client-dynamodb": "^3.872.0",
"@aws-sdk/client-secrets-manager": "^3.872.0",
"@aws-sdk/lib-dynamodb": "^3.872.0",
"@modelcontextprotocol/sdk": "^1.25.2",
"axios": "^1.13.2",

Copilot AI Feb 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The axios version constraint uses "^1.13.2", but the latest stable version of axios is in the 1.7.x range (as of early 2025). Version 1.13.x doesn't exist. This appears to be a typo or incorrect version specification. Consider updating to a valid version like "^1.7.0" or checking the intended version.

Suggested change
"axios": "^1.13.2",
"axios": "^1.7.0",

Copilot uses AI. Check for mistakes.
"dotenv": "^17.2.3",
Expand Down
4 changes: 4 additions & 0 deletions run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail

node lambda-web.js
Loading