Remote Agent API
Enable external AI agents to interact with OWS on behalf of users via HTTP API with bearer token authentication.
Using Claude Desktop, Claude Code, or Cursor? You can skip the REST API entirely and connect via the built-in MCP server for native tool integration — no HTTP calls needed.
Overview
The Remote Agent API lets any AI (Claude, GPT, custom bots) query training data, create exercises, log workouts, and manage programs. All actions are performed on behalf of the authenticated user and logged to their activity feed. For MCP-compatible clients, a bundled MCP server provides the same tools via stdio transport.
Authentication
Users generate API tokens from their Account page. Tokens use the ows_ prefix and are shown only once (like GitHub PATs). The SHA-256 hash is stored — the plaintext cannot be recovered.
# Include the token in every request curl -H "Authorization: Bearer ows_your_token_here" \ https://your-domain.com/api/remote/tools
Endpoints
GET /api/remote/tools
Returns the full tool manifest — all available tools with their schemas. Use this to discover what actions your agent can perform.
# Response
{
"tools": [
{
"name": "query_user_data",
"description": "Fetch user data by type...",
"parameters": { ... }
},
...
],
"count": 42
}GET /api/remote/context
Returns the user's profile, fitness data, nutrition info, recent activity, and saved notes. Useful for giving your agent context before executing tools. Each data source loads independently — if one fails, the others still return with a warnings array describing what failed.
# Response
{
"profile": {
"personal": { "fullName": "...", "gender": "..." },
"fitness": { "weight": 80, "fitnessLevel": "intermediate", ... },
"settings": { "units": "metric", "timezone": "America/New_York" },
"stats": { ... },
"nutritionBlueprint": { ... }
},
"recentActivity": [ ... ],
"notes": [ ... ],
"warnings": [] // only present if a data source partially failed
}POST /api/remote/execute
Execute a single tool call. This is the core endpoint — it runs the same tool executor used by the built-in AI chat, so all tools behave identically.
# Request
POST /api/remote/execute
Content-Type: application/json
Authorization: Bearer ows_...
{
"tool": "query_user_data",
"arguments": {
"dataType": "personal_profile"
}
}
# Response
{
"success": true,
"tool": "query_user_data",
"result": {
"summary": "[query_user_data: personal_profile → 1 items]...",
"data": null
}
}POST /api/remote/chat
Save messages to the remote chat history. This is separate from the built-in Grok chat — use it to persist your agent's conversation with the user.
# Save a message
POST /api/remote/chat
{
"role": "user",
"content": "What did I eat today?"
}GET /api/remote/chat
Retrieve the remote chat history. Returns messages in chronological order.
# Query params: ?limit=50 (default, max 200)
# Response
{
"messages": [
{ "id": "rmsg-...", "role": "user", "content": "...", "createdAt": "..." },
...
],
"count": 12
}DELETE /api/remote/chat
Clear all remote chat messages.
Token Management
These endpoints do not require bearer auth — they use a userId parameter and are meant to be called from the account page UI.
POST /api/remote/token
# Generate a new token (revokes existing one)
{ "userId": "abc123", "label": "My Claude Agent" }
# Response — save this token, it won't be shown again!
{ "token": "ows_a3f1b2c4d5...", "prefix": "ows_a3f1b2c4", "message": "..." }DELETE /api/remote/token
# Revoke all tokens
{ "userId": "abc123" }GET /api/remote/token?userId=abc123
Check if a user has an active token.
Code Examples
Python
import requests
BASE = "https://your-domain.com/api/remote"
TOKEN = "ows_your_token_here"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
# Get available tools
tools = requests.get(f"{BASE}/tools", headers=HEADERS).json()
print(f"Available tools: {tools['count']}")
# Get user context
ctx = requests.get(f"{BASE}/context", headers=HEADERS).json()
print(f"User: {ctx['profile']['personal']['fullName']}")
# Execute a tool
result = requests.post(f"{BASE}/execute", headers=HEADERS, json={
"tool": "query_user_data",
"arguments": {"dataType": "fitness_profile"}
}).json()
print(result["result"]["summary"])
# Log a weight entry
result = requests.post(f"{BASE}/execute", headers=HEADERS, json={
"tool": "log_weight",
"arguments": {"weightKg": 81.2}
}).json()
print(result["result"]["summary"])
# Full workout flow: load → configure → log sets
requests.post(f"{BASE}/execute", headers=HEADERS, json={
"tool": "load_exercise",
"arguments": {"exerciseId": "lib-xxx", "exerciseName": "Bench Press"}
})
requests.post(f"{BASE}/execute", headers=HEADERS, json={
"tool": "configure_queue_item",
"arguments": {"itemName": "bench press", "sets": 3, "reps": 10, "weight": 60}
})
for s in range(1, 4):
requests.post(f"{BASE}/execute", headers=HEADERS, json={
"tool": "log_completed_set",
"arguments": {"exerciseId": "lib-xxx", "setNumber": s, "reps": 10, "weight": 60}
})JavaScript / TypeScript
const BASE = "https://your-domain.com/api/remote";
const TOKEN = "ows_your_token_here";
const headers = { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" };
// Get tools
const { tools } = await fetch(`${BASE}/tools`, { headers }).then(r => r.json());
// Execute a tool
const result = await fetch(`${BASE}/execute`, {
method: "POST",
headers,
body: JSON.stringify({
tool: "search_exercises",
arguments: { query: "squat", limit: 5 }
})
}).then(r => r.json());
console.log(result.result.summary);curl
# List tools
curl -H "Authorization: Bearer ows_..." \
https://your-domain.com/api/remote/tools
# Get context
curl -H "Authorization: Bearer ows_..." \
https://your-domain.com/api/remote/context
# Execute tool
curl -X POST \
-H "Authorization: Bearer ows_..." \
-H "Content-Type: application/json" \
-d '{"tool":"query_user_data","arguments":{"dataType":"today_nutrition"}}' \
https://your-domain.com/api/remote/executeTool Reference
All tools below are available via POST /api/remote/execute and through the MCP server. Pass the tool name and arguments as shown.
Data & Query
query_user_data
Fetch user data by type. Supports: weight_history, activity_log, my_exercises, dashboard_queue, today_nutrition, nutrition_by_date (requires date param), fitness_profile, personal_profile, my_sessions, my_routes, programs.
{
"tool": "query_user_data",
"arguments": { "dataType": "fitness_profile" }
}
// With params (e.g. past date nutrition):
{
"tool": "query_user_data",
"arguments": { "dataType": "nutrition_by_date", "date": "2026-03-15" }
}query_exercise_logs
Query workout history. Filter by exercise, date range, or get recent logs. Returns sets with reps, weight, RPE, timestamps.
{
"tool": "query_exercise_logs",
"arguments": {
"exerciseId": "lib-xxx",
"startDate": "2026-03-01",
"endDate": "2026-03-28",
"limit": 50
}
}get_personal_records
Get personal records for an exercise or across all exercises. Returns best weight, most reps, highest volume, etc.
{
"tool": "get_personal_records",
"arguments": { "exerciseId": "lib-xxx" }
}get_exercise
Fetch full exercise details by ID or name — includes type, muscles, defaults, trackable metrics, intent data, and AI notes.
{
"tool": "get_exercise",
"arguments": { "exerciseName": "barbell back squat" }
}search_exercises
Search the exercise library by name, equipment, muscles, or tags. Returns matching exercises with IDs.
{
"tool": "search_exercises",
"arguments": { "query": "chest press", "limit": 5 }
}Workout Execution
load_exercise
Load a single exercise into the dashboard queue. Pass exerciseId and exerciseName.
{
"tool": "load_exercise",
"arguments": {
"exerciseId": "lib-xxx",
"exerciseName": "Bench Press"
}
}load_session
Load a saved session (routine) into the dashboard queue. All exercises in the session are loaded together.
{
"tool": "load_session",
"arguments": { "sessionId": "lib-session-xxx" }
}load_todays_workout
Load the current day's workout from the active program. Automatically calculates the correct week/day from the program's start date. Resolves exercises and writes the session to the dashboard queue.
{
"tool": "load_todays_workout",
"arguments": {}
}Week/day is calculated from startDate, not a stored pointer. If advanceDay is omitted/true, the stored pointer syncs to match.
configure_queue_item
Modify parameters of an exercise in the dashboard queue — sets, reps, weight, duration, rest. Works for standalone items and exercises inside loaded sessions.
{
"tool": "configure_queue_item",
"arguments": {
"itemName": "bench press",
"sets": 4, "reps": 8, "weight": 70, "restSeconds": 90
}
}
// Per-set overrides (pyramid sets):
{
"tool": "configure_queue_item",
"arguments": {
"itemName": "squat",
"setOverrides": {
"1": { "reps": 12, "weight": 60 },
"2": { "reps": 10, "weight": 70 },
"3": { "reps": 8, "weight": 80 }
}
}
}log_completed_set
Log a single completed exercise set. Weight is always in kg. Returns the log ID.
{
"tool": "log_completed_set",
"arguments": {
"exerciseId": "lib-xxx",
"setNumber": 1,
"reps": 10, "weight": 60, "rpe": 7,
"status": "completed"
}
}Optional: durationSecs, distance, isWarmup, notes, sessionId/sessionName. Pass exerciseName instead of exerciseId for best-match lookup.
log_completed_sets
Batch log multiple sets at once. More efficient than calling log_completed_set repeatedly.
{
"tool": "log_completed_sets",
"arguments": {
"exerciseId": "lib-xxx",
"sets": [
{ "setNumber": 1, "reps": 10, "weight": 60 },
{ "setNumber": 2, "reps": 10, "weight": 60 },
{ "setNumber": 3, "reps": 8, "weight": 60, "rpe": 9 }
]
}
}remove_exercise_log
Delete one or more set logs by ID. Use query_exercise_logs first to find log IDs.
{
"tool": "remove_exercise_log",
"arguments": { "logIds": ["abc123", "def456"] }
}remove_from_queue
Remove an item from the dashboard queue by item ID or name.
{
"tool": "remove_from_queue",
"arguments": { "itemName": "bench press" }
}reorder_queue
Reorder items in the dashboard queue. Pass the full ordered list of item IDs.
{
"tool": "reorder_queue",
"arguments": { "orderedIds": ["item-1", "item-2", "item-3"] }
}manage_queue_group
Create, update, or delete superset/circuit groups in the dashboard queue.
{
"tool": "manage_queue_group",
"arguments": {
"action": "create",
"groupName": "Chest Superset",
"groupType": "superset",
"itemIds": ["item-1", "item-2"]
}
}move_queue_item
Move a queue item to a specific position or into/out of a group.
{
"tool": "move_queue_item",
"arguments": { "itemId": "item-1", "targetIndex": 0 }
}Exercise Management
create_exercise_draft
Create a new exercise. By default shows a draft for user review. Set autoAccept: true to save immediately (useful when creating simple exercises as part of a larger workflow like program building).
{
"tool": "create_exercise_draft",
"arguments": {
"name": "Brisk Walk",
"autoAccept": true,
"description": "Steady-pace walking for cardiovascular health",
"experienceLevelRecommended": "beginner",
"primaryMuscles": ["quadriceps", "calves"],
"instructions": [
{ "title": "Start", "detail": "Begin walking at a brisk pace" },
{ "title": "Pace", "detail": "Maintain a pace where you can talk but not sing" },
{ "title": "Finish", "detail": "Cool down with a slower pace for the last minute" }
],
"safetyNotes": "Wear supportive shoes. Stay hydrated.",
"defaults": { "sets": 1, "durationSeconds": 900 },
"trackingPreset": "duration",
"youtubeSearchQuery": "brisk walking technique",
"modality": "cardio",
"metValue": 3.5
}
}Required fields: name, description, experienceLevelRecommended, primaryMuscles, instructions, safetyNotes, defaults, youtubeSearchQuery, modality, metValue. The name should be the canonical movement name — never embed duration/sets/reps in it.
bulk_create_exercises
Create multiple exercises at once — all auto-accepted (no user review). Skips duplicates by name and returns the existing ID. Much faster than sequential create_exercise_draft calls. Use when populating a program with many new exercises.
{
"tool": "bulk_create_exercises",
"arguments": {
"exercises": [
{
"name": "Bulgarian Split Squat",
"modality": "resistance", "metValue": 6,
"primaryMuscles": ["quadriceps", "glutes"],
"equipment": ["bench"],
"defaults": { "sets": 3, "reps": 10 }
},
{
"name": "Face Pull",
"modality": "resistance", "metValue": 4,
"primaryMuscles": ["rear delts", "upper back"],
"equipment": ["cable machine"],
"defaults": { "sets": 3, "reps": 15 }
},
{
"name": "Nordic Curl",
"modality": "resistance", "metValue": 5,
"primaryMuscles": ["hamstrings"],
"equipment": ["none"],
"defaults": { "sets": 3, "reps": 6 }
}
]
}
}Only name, modality, and metValue are required per exercise — other fields get sensible defaults. Returns a map of name → exercise ID for all exercises (created and existing). Existing exercises are auto-added to the user's collection.
update_exercise_draft
Update an existing exercise. Creates a draft with the merged changes for user review.
{
"tool": "update_exercise_draft",
"arguments": {
"exerciseId": "lib-xxx",
"description": "Updated description",
"safetyNotes": "Updated safety notes"
}
}save_exercise_note
Save an AI observation tied to a specific exercise. Notes persist across conversations. If the exercise isn't in the user's collection, it's auto-added.
{
"tool": "save_exercise_note",
"arguments": {
"exerciseName": "push-ups",
"note": "User prefers 20-25 reps, finds low rep ranges boring",
"context": "general"
}
}delete_exercise_note
Delete an AI note from an exercise by index or content match.
{
"tool": "delete_exercise_note",
"arguments": { "exerciseName": "push-ups", "noteSubstring": "shoulder" }
}Session Management
create_session
Create a workout session (routine). Returns a draft for user review. Exercises are resolved by name — unknown exercises are created as placeholders.
{
"tool": "create_session",
"arguments": {
"name": "Upper Body Push",
"description": "Chest and shoulder focused session",
"exercises": [
{ "exerciseName": "Bench Press", "sets": 4, "reps": 8, "weight": 60 },
{ "exerciseName": "Overhead Press", "sets": 3, "reps": 10, "weight": 30 },
{ "exerciseName": "Tricep Dips", "sets": 3, "reps": 12 }
],
"estimatedDuration": 45,
"experienceLevelRecommended": "intermediate"
}
}update_session
Update an existing session — modify exercises, name, description, or other fields.
{
"tool": "update_session",
"arguments": {
"sessionId": "lib-session-xxx",
"exercises": [
{ "exerciseName": "Bench Press", "sets": 5, "reps": 5, "weight": 70 }
]
}
}Program Management
create_program
Create a multi-week training program with day-by-day exercise plans. Returns a draft for user review.
{
"tool": "create_program",
"arguments": {
"name": "12-Week Strength Builder",
"description": "Progressive overload program",
"goal": "Build strength",
"daysPerWeek": 4,
"totalWeeks": 12,
"split": "Upper/Lower",
"weeks": [ ... ]
}
}get_active_program
Fetch the user's active program. By default returns the current week + next week (calculated from startDate). Set includeAllWeeks: true for the full program.
{
"tool": "get_active_program",
"arguments": { "includeAllWeeks": false }
}Week/day is calculated from the program's startDate, not a stored pointer. The response includes currentWeek and currentDay based on today's date.
update_program
Update the active program metadata, phases, or full weeks array. Supports extending programs — set totalWeeks to a higher number and empty weeks are auto-created. If phases sum exceeds totalWeeks, it auto-extends. If sending a weeks array, totalWeeks auto-adjusts to match.
// Extend program from 12 to 16 weeks (empty weeks auto-created):
{
"tool": "update_program",
"arguments": { "totalWeeks": 16 }
}
// Update phases (auto-extends totalWeeks if sum > current):
{
"tool": "update_program",
"arguments": {
"phases": [
{ "name": "Build Base", "weeks": 4 },
{ "name": "Intensity", "weeks": 6 },
{ "name": "Deload", "weeks": 1 },
{ "name": "Peak", "weeks": 1 },
{ "name": "Sport Specific", "weeks": 4 }
]
}
}To extend a program: set totalWeeks (empty weeks auto-created), then use bulk_patch_program to populate the new weeks. This is faster than rebuilding the full weeks array.
patch_program_day
Update a single day within a program. Uses a Firestore transaction for atomic writes — safe for concurrent use. Best for 1-3 day changes. For bulk changes, use bulk_patch_program instead.
{
"tool": "patch_program_day",
"arguments": {
"weekNumber": 1,
"dayNumber": 3,
"exercises": [
{ "exerciseName": "Bench Press", "sets": 4, "reps": 8 },
{ "exerciseName": "Rows", "sets": 4, "reps": 8 }
]
}
}Omit programId to target the active program. Omit exercises to keep existing exercises unchanged. Exercise names are resolved to IDs automatically.
bulk_patch_program
The primary tool for bulk program modifications. Uses scope-based targeting to match days by pattern, then applies actions atomically. Supports adding, removing, replacing, and modifying exercises, plus changing day-level properties. All changes happen in a single Firestore transaction.
// Add "Brisk Walk" to the start of EVERY day (one call):
{
"tool": "bulk_patch_program",
"arguments": {
"scope": { "match": "all" },
"prependExercises": [
{ "exerciseName": "Brisk Walk", "sets": 1, "duration": 900 }
]
}
}
// Drop sets by 1 on all exercises during Deload phase:
{
"tool": "bulk_patch_program",
"arguments": {
"scope": { "match": "phase", "phase": "Deload" },
"modifyExercises": {
"target": "all",
"delta": { "sets": -1 }
}
}
}
// Replace "Cardio" with "Brisk Walk" everywhere:
{
"tool": "bulk_patch_program",
"arguments": {
"scope": { "match": "all" },
"replaceExercise": {
"old": "Cardio",
"new": { "exerciseName": "Brisk Walk", "sets": 1, "duration": 900 }
}
}
}
// Make rest days active recovery with a walk:
{
"tool": "bulk_patch_program",
"arguments": {
"scope": { "match": "rest" },
"setDayProperties": { "isRest": false },
"appendExercises": [
{ "exerciseName": "Brisk Walk", "sets": 1, "duration": 900 }
]
}
}
// Set squats to 80kg in weeks 5-8:
{
"tool": "bulk_patch_program",
"arguments": {
"scope": { "match": "weeks", "weekRange": [5, 8] },
"modifyExercises": {
"target": { "name": "Squats" },
"patch": { "weight": 80 }
}
}
}
// Add sprints to every Monday (day 1):
{
"tool": "bulk_patch_program",
"arguments": {
"scope": { "match": "dayNumber", "dayNumbers": [1] },
"appendExercises": [
{ "exerciseName": "Sprints", "sets": 5, "duration": 30 }
]
}
}Scope match types: all (every day), phase (by phase name), dayNumber (specific day-of-week), dayName (fuzzy match on day name), rest (only rest days), training (only non-rest days), weeks (specific weeks or weekRange). Actions applied in order: setDayProperties → removeExercises → replaceExercise → modifyExercises → prependExercises → appendExercises. replaceExercise silently skips days where the old exercise isn't found. modifyExercises.delta applies relative changes (e.g. {sets: -1} reduces by 1, clamped to >= 0). modifyExercises.patch sets absolute values. Response includes a detailed summary: "42/84 days modified, 42 exercises added, 42 exercises removed". Legacy patches mode (explicit weekNumber/dayNumber array) is still supported as a fallback when scope can't express what you need.
remap_program_exercise
Fix an incorrect exercise mapping across the entire program. Swaps all occurrences of oldExerciseId with newExerciseId in every week/day.
{
"tool": "remap_program_exercise",
"arguments": {
"oldExerciseId": "lib-wrong-id",
"newExerciseId": "lib-correct-id",
"newExerciseName": "Correct Exercise Name"
}
}list_programs
List all programs for the user. Returns program metadata (no weeks data).
{
"tool": "list_programs",
"arguments": {}
}delete_program
Delete a program by ID.
{
"tool": "delete_program",
"arguments": { "programId": "program-xxx" }
}Daily Logging
log_weight
Log body weight. Always in kg. Auto-updates the user's fitness profile.
{
"tool": "log_weight",
"arguments": { "weightKg": 80.5 }
}log_water
Log water intake in ml.
{
"tool": "log_water",
"arguments": { "amountMl": 500 }
}log_steps
Log step count. Adds to today's total by default, or set mode='set' to replace.
{
"tool": "log_steps",
"arguments": { "steps": 5000, "note": "morning walk" }
}set_daily_targets
Set daily targets for water, calories, steps, and weigh-in schedule. Only provided fields change.
// Set step target and weigh-in schedule:
{
"tool": "set_daily_targets",
"arguments": {
"stepTarget": 8000,
"weighInSchedule": "custom",
"weighInDays": [1, 4]
}
}
// "Only remind me to weigh in on Sundays":
{
"tool": "set_daily_targets",
"arguments": {
"weighInSchedule": "custom",
"weighInDays": [0]
}
}weighInDays uses JS day numbers: 0=Sun, 1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri, 6=Sat. weighInSchedule='daily' shows weigh-in every day (default).
log_macros
Log a food/macro entry with calories, protein, carbs, fat, and optional food name.
{
"tool": "log_macros",
"arguments": {
"foodName": "Chicken breast",
"calories": 165,
"proteinG": 31,
"carbsG": 0,
"fatG": 3.6,
"servingSize": "100g"
}
}remove_macro_entry
Delete a food/macro entry by ID. Use query_user_data with today_nutrition to find entry IDs.
{
"tool": "remove_macro_entry",
"arguments": { "entryId": "macro-entry-xxx" }
}remove_water_entry
Delete a water entry by ID.
{
"tool": "remove_water_entry",
"arguments": { "entryId": "water-entry-xxx" }
}Goals
set_goal
Create a new fitness goal with a target value, deadline, and category.
{
"tool": "set_goal",
"arguments": {
"name": "Lose 5kg",
"category": "weight_loss",
"targetValue": 75,
"unit": "kg",
"deadline": "2026-06-01"
}
}update_goal
Update an existing goal's target, deadline, status, or other fields.
{
"tool": "update_goal",
"arguments": {
"goalId": "goal-xxx",
"targetValue": 73,
"deadline": "2026-07-01"
}
}get_goal_progress
Get progress toward a specific goal or all active goals. Includes current value, percent complete, and trend.
{
"tool": "get_goal_progress",
"arguments": { "goalId": "goal-xxx" }
}Nutrition
set_nutrition_blueprint
Set the user's daily nutrition targets — calories, protein, carbs, fat, water.
{
"tool": "set_nutrition_blueprint",
"arguments": {
"calories": 2200,
"proteinG": 160,
"carbsG": 220,
"fatG": 70,
"waterMl": 3000
}
}set_nutrition_phases
Set phased nutrition plans (e.g. cut → maintain → bulk) with date ranges and macro targets per phase.
{
"tool": "set_nutrition_phases",
"arguments": {
"phases": [
{ "name": "Cut", "startDate": "2026-03-01", "endDate": "2026-05-31", "calories": 1800, "proteinG": 170 },
{ "name": "Maintain", "startDate": "2026-06-01", "endDate": "2026-08-31", "calories": 2200, "proteinG": 150 }
]
}
}Milestones & Notes
create_milestone
Create a milestone to mark a significant achievement (PR, streak, body composition goal reached, etc.).
{
"tool": "create_milestone",
"arguments": {
"title": "First 100kg Squat",
"description": "Hit 100kg for 3 reps on back squat",
"category": "strength"
}
}save_user_note
Save a user preference or context note. These persist across conversations and are injected into the AI system prompt.
{
"tool": "save_user_note",
"arguments": {
"note": "User prefers morning workouts and has a left shoulder impingement — avoid overhead pressing"
}
}Body Composition
analyze_progress_photos
Analyze progress photos using AI vision. Compares photos across dates to assess body composition changes.
{
"tool": "analyze_progress_photos",
"arguments": { "dateRange": "last_30_days" }
}save_body_fat_estimate
Save an AI-estimated body fat percentage for a specific date/photo.
{
"tool": "save_body_fat_estimate",
"arguments": {
"estimate": 18.5,
"method": "ai_visual",
"date": "2026-03-28"
}
}Hardware & Devices
create_device_program
Create a programmable sequence for a connected hardware device (smart cable machine, etc.). Defines steps with target resistance, speed, or position.
{
"tool": "create_device_program",
"arguments": {
"deviceId": "device-xxx",
"name": "Drop Set Protocol",
"steps": [
{ "resistance": 50, "duration": 30 },
{ "resistance": 35, "duration": 30 },
{ "resistance": 20, "duration": 30 }
]
}
}suggest_hardware_enhancement
Suggest how a connected device could enhance an exercise. Creates a hardware enhancement proposal for the exercise.
{
"tool": "suggest_hardware_enhancement",
"arguments": {
"exerciseId": "lib-xxx",
"deviceId": "device-xxx",
"enhancement": "Variable resistance curve — lighter at bottom, heavier at lockout"
}
}Feedback & Bugs
report_bug
Report a bug or issue encountered by the user or agent.
{
"tool": "report_bug",
"arguments": {
"title": "Program day not loading",
"description": "load_todays_workout returns empty for Week 4 Day 1"
}
}save_feedback
Save user feedback about the app or AI experience.
{
"tool": "save_feedback",
"arguments": {
"feedback": "The bulk program update is much faster now",
"category": "positive"
}
}Data Query Types
The query_user_data tool supports these data types:
weight_historyLast 90 days of weight entries with trend (changeKg, earliest/latest)activity_logRecent activity feed from all sources. Accepts optional limit param (default 50, max 100)my_exercisesUser's exercises with aiNotes array (exercise-specific AI observations)my_sessionsUser's saved workout sessions/routinesmy_routesUser's saved GPS/cardio routesprogramsUser's training programs (metadata only, no weeks)dashboard_queueCurrent activity queue items with set completion statustoday_nutritionToday's food log, macros, water, weightnutrition_by_datePast date nutrition (requires date param YYYY-MM-DD)fitness_profileWeight, goals, training experiencepersonal_profileName, age, health notes, injuriesRate Limiting
100 requests per hour per token. Every response includes rate limit headers:
X-RateLimit-Limit100 (requests per window)X-RateLimit-RemainingRequests left in current windowX-RateLimit-ResetUnix timestamp when the window resetsError Responses
# Error response format
{
"error": "Description of what went wrong"
}MCP Setup
Connect MCP-compatible clients (Claude Desktop, Claude Code, Cursor) directly to OWS using the bundled MCP server. It proxies all requests to the REST API above.
Install
The MCP server is available on npm — no build step needed. Just add the config below and your client will auto-install it via npx.
Claude Desktop
Add the following to your claude_desktop_config.json:
{
"mcpServers": {
"ows": {
"command": "npx",
"args": ["-y", "ows-mcp-server"],
"env": {
"OWS_API_TOKEN": "ows_your_token_here",
"OWS_BASE_URL": "https://www.openworkoutsystem.com"
}
}
}
}Claude Code
Add to .claude/mcp.json in your project root:
{
"mcpServers": {
"ows": {
"command": "npx",
"args": ["-y", "ows-mcp-server"],
"env": {
"OWS_API_TOKEN": "ows_your_token_here",
"OWS_BASE_URL": "https://www.openworkoutsystem.com"
}
}
}
}Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"ows": {
"command": "npx",
"args": ["-y", "ows-mcp-server"],
"env": {
"OWS_API_TOKEN": "ows_your_token_here",
"OWS_BASE_URL": "https://www.openworkoutsystem.com"
}
}
}
}Available MCP Resources
The MCP server exposes three resources that clients can read for context:
ows://profileUser profile, fitness data, and settingsows://activity20 most recent activity log entriesows://notesSaved user notes and preferencesActivity Logging
All tool executions via the Remote Agent API are automatically logged to the user's activity feed with source: "remote_api". Users can see these entries in their activity log alongside actions from the built-in AI chat and UI.