Skip to main content
Glama

mcp-server-vanmoof

server.pyβ€’20.1 kB
from mcp.server.fastmcp import FastMCP import requests from dataclasses import dataclass from typing import Dict, Any import os import base64 from dotenv import load_dotenv from tzlocal import get_localzone import locale from datetime import datetime, timedelta import re # Load variables from .env file into the environment load_dotenv() # Get the local timezone object local_tz = get_localzone() # Get the IANA timezone name (e.g., 'Europe/Amsterdam') timezone_name = str(local_tz) # Get the default locale settings (language code, encoding) lang_code, encoding = locale.getdefaultlocale() # Create the MCP server mcp = FastMCP("vanMoof") # vanMoof API credentials VANMOOF_USERNAME = os.getenv("VANMOOF_USERNAME") if not VANMOOF_USERNAME: raise ValueError("VANMOOF_USERNAME environment variable is not set.") VANMOOF_PASSWORD = os.getenv("VANMOOF_PASSWORD") if not VANMOOF_PASSWORD: raise ValueError("VANMOOF_PASSWORD environment variable is not set.") @dataclass class VanMoofAPI: def get_vanmoof_token(username, password): """ Authenticates with the VanMoof API and retrieves an access token. Args: username: The user's VanMoof email address. password: The user's VanMoof password. Returns: The access token string if authentication is successful, otherwise None. """ api_key = 'fcb38d47-f14b-30cf-843b-26283f6a5819' uri_prefix = 'https://my.vanmoof.com/api/v8' auth_url = f'{uri_prefix}/authenticate' # Prepare Basic Authentication credentials auth_string = f"{username}:{password}" encoded_auth_string = base64.b64encode(auth_string.encode('ascii')).decode('ascii') headers = { 'Authorization': f'Basic {encoded_auth_string}', 'Api-Key': api_key } try: response = requests.post(auth_url, headers=headers) response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx) # Parse the JSON response and extract the token token = response.json().get('token') return token except requests.exceptions.RequestException as e: print(f"An error occurred during the API request: {e}") return None except Exception as e: print(f"An unexpected error occurred: {e}") return None def get_application_token(token): """ Authenticates with the VanMoof API and retrieves an application token. Args: token: The user's VanMoof access token. Returns: The application token string if authentication is successful, otherwise None. """ api_key = 'fcb38d47-f14b-30cf-843b-26283f6a5819' uri_prefix = 'https://api.vanmoof-api.com/v8' auth_url = f'{uri_prefix}/getApplicationToken' headers = { 'authorization': f'Bearer {token}', "accept": "*/*", "user-agent": "VanMoof-Rider/23.7 (sdk_gphone_x86_arm, Android 11)", 'api-key': api_key } try: response = requests.get(auth_url, headers=headers) response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx) # Parse the JSON response and extract the token application_token = response.json() return application_token.get('token') except requests.exceptions.RequestException as e: print(f"An error occurred during the API request: {e}") return None except Exception as e: print(f"An unexpected error occurred: {e}") return None @mcp.tool() # Function to get customer data def get_customer_data() -> Dict[str, Any]: """ Retrieves customer data from the vanMoof API. Returns: The rider vanMoof's customer data if authentication is successful, otherwise None. """ # Get the Bearer token from the authenticate method token = VanMoofAPI.get_vanmoof_token(VANMOOF_USERNAME, VANMOOF_PASSWORD) if not token: return {"error": "Authentication failed"} url = "https://my.vanmoof.com/api/v8/getCustomerData" headers = { "authorization": f"Bearer {token}", "api-key": "fcb38d47-f14b-30cf-843b-26283f6a5819" } response = requests.get(url, headers=headers) return response.json() @mcp.tool() # Function to get vanMoof Cities def get_vanmoof_cities() -> Dict[str, Any]: """ Retrieves a list of city data from the vanMoof API. Returns: The rider vanMoof's city data if authentication is successful, otherwise None. """ # Get the Bearer token from the authenticate method token = VanMoofAPI.get_vanmoof_token(VANMOOF_USERNAME, VANMOOF_PASSWORD) application_token = VanMoofAPI.get_application_token(token) if not application_token: return {"error": "Authentication failed"} url = "https://tenjin.vanmoof.com/api/v1/cities" headers = { "authorization": f"Bearer {application_token}", "api-key": "fcb38d47-f14b-30cf-843b-26283f6a5819" } response = requests.get(url, headers=headers) return response.json() @mcp.tool() # Function to get rider preferences def get_rider_preferences() -> Dict[str, Any]: """ Retrieves rider preferences from the vanMoof API. Returns: The rider preferences if authentication is successful, otherwise None. """ # Get the Bearer token from the authenticate method token = VanMoofAPI.get_vanmoof_token(VANMOOF_USERNAME, VANMOOF_PASSWORD) application_token = VanMoofAPI.get_application_token(token) if not application_token: return {"error": "Authentication failed"} # Get the riderId from the customer data # path to rider is data.uuid in the customer data json response riderId = VanMoofAPI.get_customer_data().get('data', {}).get('uuid') if not riderId: return {"error": "RiderId not found"} url = f"https://tenjin.vanmoof.com/api/v1/riders/{riderId}/preferences" headers = { "authorization": f"Bearer {application_token}", "api-key": "fcb38d47-f14b-30cf-843b-26283f6a5819" } response = requests.get(url, headers=headers) return response.json() #@mcp.tool() # def get_weekly_rides(last_seen_week: str = None, limit: int = 5) -> Dict[str, Any]: # """ # Retrieves weekly rides from the vanMoof API. # Args: # last_seen_week: The date of the last seen week in format "YYYY-MM-DD". # If None, uses the current date. # limit: The maximum number of weekly ride results to return. Defaults to 5. # Returns: # The weekly rides if authentication is successful, otherwise None. # """ # # Get the Bearer token from the authenticate method # token = VanMoofAPI.get_vanmoof_token(VANMOOF_USERNAME, VANMOOF_PASSWORD) # application_token = VanMoofAPI.get_application_token(token) # if not application_token: # return {"error": "Authentication failed"} # # Get the riderId from the customer data # customerData = VanMoofAPI.get_customer_data() # riderId = customerData.get('data', {}).get('uuid') # if not riderId: # return {"error": "RiderId not found"} # bikeId = customerData.get('data', {}).get('bikes', [{}])[0].get('id') # if not bikeId: # return {"error": "BikeId not found"} # country = customerData.get('data', {}).get('country') # if not country: # return {"error": "CountryCode not found"} # # If last_seen_week is not provided, use current date # if last_seen_week is None: # last_seen_week = datetime.now().strftime("%Y-%m-%d") # # Ensure last_seen_week is the Monday of the week # date_obj = datetime.strptime(last_seen_week, "%Y-%m-%d") # monday = date_obj - timedelta(days=date_obj.weekday()) # last_seen_week = monday.strftime("%Y-%m-%d") # url = f"https://tenjin.vanmoof.com/api/v1/rides/{riderId}/{bikeId}/weekly" # querystring = {"lastSeenWeek": last_seen_week, "limit": str(limit)} # headers = { # "authorization": f"Bearer {application_token}", # "api-key": "fcb38d47-f14b-30cf-843b-26283f6a5819", # "cache-control": "no-cache, private", # "accept-language": f"{country.lower()}_{country.upper()}", # "accept-encoding": "gzip", # "timezone": timezone_name, # "accept": "*/*", # } # response = requests.get(url, headers=headers, params=querystring) # return response.json() @mcp.tool() def get_rides_summary()-> Dict[str, Any]: """ Retrieves total rides summary for the VanMoof rider being authenticated. Returns: The a summary of the total rides of the VanMoof rider if authentication is successful, otherwise None. The following information is returned: - Average distance in km - Total Rides - Average duration in minutes - Total distance in km """ # Get the Bearer token from the authenticate method token = VanMoofAPI.get_vanmoof_token(VANMOOF_USERNAME, VANMOOF_PASSWORD) application_token = VanMoofAPI.get_application_token(token) if not application_token: return {"error": "Authentication failed"} # Get the riderId from the customer data customerData = VanMoofAPI.get_customer_data() riderId = customerData.get('data', {}).get('uuid') if not riderId: return {"error": "RiderId not found"} bikeId = customerData.get('data', {}).get('bikes', [{}])[0].get('id') if not bikeId: return {"error": "BikeId not found"} country = customerData.get('data', {}).get('country') if not country: return {"error": "CountryCode not found"} last_seen_week = datetime.now().strftime("%Y-%m-%d") # Ensure last_seen_week is the Monday of the week date_obj = datetime.strptime(last_seen_week, "%Y-%m-%d") monday = date_obj - timedelta(days=date_obj.weekday()) last_seen_week = monday.strftime("%Y-%m-%d") url = f"https://tenjin.vanmoof.com/api/v1/rides/{riderId}/{bikeId}/weekly" querystring = {"lastSeenWeek": last_seen_week, "limit": str(1)} headers = { "authorization": f"Bearer {application_token}", "api-key": "fcb38d47-f14b-30cf-843b-26283f6a5819", "cache-control": "no-cache, private", "accept-language": f"{country.lower()}_{country.upper()}", "accept-encoding": "gzip", "timezone": timezone_name, "accept": "*/*", } response = requests.get(url, headers=headers, params=querystring) return response.json().get('carousel', {}).get('summary', {}) @mcp.tool() def get_rides_for_week(date_in_week: str = "") -> Dict[str, Any]: """ Retrieves rides for a specific week from the vanMoof API. Args: date_in_week: Any date within the week in format "YYYY-MM-DD". If None, uses the current date. Returns: The rides for the specified week if authentication is successful, otherwise None. """ # If date_in_week is not provided, return error if date_in_week == "": return {"error": "Missing Argument. Please use YYYY-MM-DD format."} # Validate date format (YYYY-MM-DD) try: # Check basic format with regex if not re.match(r'^\d{4}-\d{2}-\d{2}$', date_in_week): return {"error": "Invalid date format. Please use YYYY-MM-DD format."} # Try to parse the date to validate it's a real date date_obj = datetime.strptime(date_in_week, "%Y-%m-%d") except ValueError: return {"error": "Invalid date. Please provide a valid date in YYYY-MM-DD format."} # Get the Bearer token from the authenticate method token = VanMoofAPI.get_vanmoof_token(VANMOOF_USERNAME, VANMOOF_PASSWORD) application_token = VanMoofAPI.get_application_token(token) if not application_token: return {"error": "Authentication failed"} # Get the riderId and bikeId from the customer data customerData = VanMoofAPI.get_customer_data() riderId = customerData.get('data', {}).get('uuid') if not riderId: return {"error": "RiderId not found"} bikeId = customerData.get('data', {}).get('bikes', [{}])[0].get('id') if not bikeId: return {"error": "BikeId not found"} country = customerData.get('data', {}).get('country') if not country: return {"error": "CountryCode not found"} # Calculate the Monday (start) and Sunday (end) of the week monday = date_obj - timedelta(days=date_obj.weekday()) sunday = monday + timedelta(days=7) # The API needs the end date of the week as lastSeenWeek last_seen_week = sunday.strftime("%Y-%m-%d") url = f"https://tenjin.vanmoof.com/api/v1/rides/{riderId}/{bikeId}/weekly" querystring = {"lastSeenWeek": last_seen_week, "limit": "1"} headers = { "authorization": f"Bearer {application_token}", "api-key": "fcb38d47-f14b-30cf-843b-26283f6a5819", "cache-control": "no-cache, private", "accept-language": f"{country.lower()}_{country.upper()}", "accept-encoding": "gzip", "timezone": timezone_name, "accept": "*/*", } response = requests.get(url, headers=headers, params=querystring) result = response.json().get('section', {})[0] # add section querystring to the result json result['querystring'] = querystring if not result: return {"error": "No rides found for the specified week"} else: return result @mcp.tool() def get_city_rides_thisweek()-> Dict[str, Any]: """ Retrieves total city rides summary from VanMoof riders. Returns: The a summary of the total rides of the VanMoof rider city if authentication is successful, otherwise None. The following information is returned: - City name - Average distance in km - Total Rides - Average duration in minutes - Location in latitude and longitude """ # Get the Bearer token from the authenticate method token = VanMoofAPI.get_vanmoof_token(VANMOOF_USERNAME, VANMOOF_PASSWORD) application_token = VanMoofAPI.get_application_token(token) if not application_token: return {"error": "Authentication failed"} # Get the riderId from the customer data customerData = VanMoofAPI.get_customer_data() riderId = customerData.get('data', {}).get('uuid') if not riderId: return {"error": "RiderId not found"} bikeId = customerData.get('data', {}).get('bikes', [{}])[0].get('id') if not bikeId: return {"error": "BikeId not found"} country = customerData.get('data', {}).get('country') if not country: return {"error": "CountryCode not found"} # Get the riders preferences and city riderPreferences = VanMoofAPI.get_rider_preferences() riderCity = riderPreferences.get('city') # Get city name from the city code cities = VanMoofAPI.get_vanmoof_cities() location= next((city['location'] for city in cities if city['code'] == riderCity), None) last_seen_week = datetime.now().strftime("%Y-%m-%d") # Ensure last_seen_week is the Monday of the week date_obj = datetime.strptime(last_seen_week, "%Y-%m-%d") monday = date_obj - timedelta(days=date_obj.weekday()) last_seen_week = monday.strftime("%Y-%m-%d") url = f"https://tenjin.vanmoof.com/api/v1/rides/{riderId}/{bikeId}/weekly" querystring = {"lastSeenWeek": last_seen_week, "limit": str(1)} headers = { "authorization": f"Bearer {application_token}", "api-key": "fcb38d47-f14b-30cf-843b-26283f6a5819", "cache-control": "no-cache, private", "accept-language": f"{country.lower()}_{country.upper()}", "accept-encoding": "gzip", "timezone": timezone_name, "accept": "*/*", } response = requests.get(url, headers=headers, params=querystring) result = response.json().get('carousel', {}).get('city', {}) # Convert the average duration from milliseconds to minutes. result['averageDuration'] = round(result['averageDuration'] / 1000 / 60, 2) # Add the city name to the result result['location'] = location return result @mcp.tool() def get_world_rides_thisweek()-> Dict[str, Any]: """ Retrieves total world rides summary from VanMoof riders. Returns: The a summary of the total rides of the VanMoof rider city if authentication is successful, otherwise None. The following information is returned: - Average distance in km - Total Rides - Average duration in minutes """ # Get the Bearer token from the authenticate method token = VanMoofAPI.get_vanmoof_token(VANMOOF_USERNAME, VANMOOF_PASSWORD) application_token = VanMoofAPI.get_application_token(token) if not application_token: return {"error": "Authentication failed"} # Get the riderId from the customer data customerData = VanMoofAPI.get_customer_data() riderId = customerData.get('data', {}).get('uuid') if not riderId: return {"error": "RiderId not found"} bikeId = customerData.get('data', {}).get('bikes', [{}])[0].get('id') if not bikeId: return {"error": "BikeId not found"} country = customerData.get('data', {}).get('country') if not country: return {"error": "CountryCode not found"} last_seen_week = datetime.now().strftime("%Y-%m-%d") # Ensure last_seen_week is the Monday of the week date_obj = datetime.strptime(last_seen_week, "%Y-%m-%d") monday = date_obj - timedelta(days=date_obj.weekday()) last_seen_week = monday.strftime("%Y-%m-%d") url = f"https://tenjin.vanmoof.com/api/v1/rides/{riderId}/{bikeId}/weekly" querystring = {"lastSeenWeek": last_seen_week, "limit": str(1)} headers = { "authorization": f"Bearer {application_token}", "api-key": "fcb38d47-f14b-30cf-843b-26283f6a5819", "cache-control": "no-cache, private", "accept-language": f"{country.lower()}_{country.upper()}", "accept-encoding": "gzip", "timezone": timezone_name, "accept": "*/*", } response = requests.get(url, headers=headers, params=querystring) result = response.json().get('carousel', {}).get('world', {}) # Convert the average duration from milliseconds to minutes. result['averageDuration'] = round(result['averageDuration'] / 1000 / 60, 2) return result

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/stefanstranger/mcp-server-vanmoof'

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