Skip to main content
Glama
index.ts3.88 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import dotenv from 'dotenv'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { zodToJsonSchema } from 'zod-to-json-schema'; import { StravaAuth } from './auth.js'; import { StravaClient } from './strava-client.js'; import { createActivityTools } from './tools/activities.js'; import { createAthleteTools } from './tools/athlete.js'; import { createRouteTools } from './tools/routes.js'; import { createSegmentTools } from './tools/segments.js'; import { createClubTools } from './tools/clubs.js'; import { createGearTools } from './tools/gear.js'; // Load environment variables from .env file (relative to this script's location) // This allows .env to work even when run from a different working directory const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); dotenv.config({ path: join(__dirname, '..', '.env') }); // Validate required environment variables const requiredEnvVars = ['STRAVA_CLIENT_ID', 'STRAVA_CLIENT_SECRET']; const missingEnvVars = requiredEnvVars.filter((key) => !process.env[key]); if (missingEnvVars.length > 0) { console.error(`Missing required environment variables: ${missingEnvVars.join(', ')}`); process.exit(1); } // Initialize Strava authentication const auth = new StravaAuth( process.env.STRAVA_CLIENT_ID!, process.env.STRAVA_CLIENT_SECRET! ); // Check for existing tokens or provide OAuth instructions if (process.env.STRAVA_ACCESS_TOKEN && process.env.STRAVA_REFRESH_TOKEN && process.env.STRAVA_EXPIRES_AT) { auth.setTokens( process.env.STRAVA_ACCESS_TOKEN, process.env.STRAVA_REFRESH_TOKEN, parseInt(process.env.STRAVA_EXPIRES_AT, 10) ); } else { console.error('\nNo Strava tokens found in environment variables.'); console.error('\nPlease run the setup script to authenticate:'); console.error(' npm run setup'); console.error('\nThis will guide you through the OAuth flow and save your tokens to .env'); process.exit(1); } // Initialize Strava client const client = new StravaClient(auth); // Create MCP server const server = new Server( { name: 'strava-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Combine all tools const allTools = { ...createActivityTools(client), ...createAthleteTools(client), ...createRouteTools(client), ...createSegmentTools(client), ...createClubTools(client), ...createGearTools(client), }; // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: Object.entries(allTools).map(([name, tool]) => ({ name, description: tool.description, inputSchema: zodToJsonSchema(tool.inputSchema), })), }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const toolName = request.params.name; const tool = allTools[toolName as keyof typeof allTools]; if (!tool) { throw new Error(`Unknown tool: ${toolName}`); } try { // Validate input with zod schema const validatedArgs = tool.inputSchema.parse(request.params.arguments); // Call the tool handler return await tool.handler(validatedArgs as never); } catch (error) { if (error instanceof Error) { throw new Error(`Tool execution failed: ${error.message}`); } throw error; } }); // Start server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('Strava MCP Server running on stdio'); } main().catch((error) => { console.error('Fatal error:', error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gcoombe/strava-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server