A cross-platform workout tracking app built with .NET MAUI and Blazor Hybrid. Plan workouts, log sets against a rest timer that keeps running across apps, track progress and personal records, and ask an on-device AI assistant to analyze your training. All data lives in a local SQLite database and works offline.
It also ships a web client with a Model Context Protocol (MCP) server, so any AI agent (Claude, Cursor, Copilot) can query your workout history and manage your plans.
No install, no account. Data stays in your browser.
Download - Preview - Features - Agent API (MCP) - Architecture - Tech stack - Getting started - Testing and CI
Android. Install the latest signed APK (~115 MB, Android 7.0+):
Windows. Download and extract the ZIP (~70 MB):
Requires .NET 11 Desktop Runtime (one-time, free). Extract the ZIP and run Physiquinator.exe. No installation needed. See WINDOWS-INSTALL.md for troubleshooting.
Every v* tag builds a signed APK and a Windows package via .github/workflows/release.yml. On first Android install, allow Install unknown apps for your browser. Updates keep your local SQLite data. The app checks GitHub releases on start and shows an update prompt in Settings.
The app seeds sample plans and workouts on first launch so you can explore immediately.
Live workout - Plans home - AI assistant
Activity history - Session summary - Exercise detail
Create plan - Edit plan - Settings
- In-app chat with streaming responses and 15 built-in tools: create and edit workout plans, log bodyweight, query training history and progression, and configure rest timer or application settings.
- Provider presets for OpenAI, OpenRouter, OpenCode, local Ollama instances, or any custom OpenAI-compatible endpoint.
- Clipboard bridge for closed models such as Gemini or ChatGPT that have no API access. Copy a generated prompt carrying your training context and the app's action schemas into the web chat, then paste the reply back. The app parses the actions, shows a summary of each, and applies them through the same tool registry. No API key required.
- Full parity with external AI agents via MCP (see Agent API).
- Configurable plans with custom exercises, rest intervals, and target sets and reps.
- Three logging types per exercise: weight and reps, bodyweight reps with an optional added-weight offset, or plain duration.
- Bodyweight share for calisthenics: volume counts a configurable percentage of your logged bodyweight, pre-filled from a built-in catalog (push-ups 65%, pull-ups 100%). Weighted variations such as weighted pull-ups add their plate load on top.
- Real-time set logging with weight and rep steppers, undo support, and completion summaries with total volume and newly achieved personal records.
- Session-to-session carryover: each set's weight and reps pre-fill from the same set number in your most recent session of that plan, falling back to the plan defaults where no history exists.
- Accurate interval countdown with quick-add increments, reset, and skip controls.
- On Android, a draggable floating overlay runs inside a foreground service to maintain countdown visibility across apps, with optional audio, haptics, and exact alarms that survive Doze mode.
- 53-week activity heatmap, per-exercise progression charts, automatic personal record tracking (weight, reps, volume, session duration), and schedule-aware streaks that account for rest days.
- Bodyweight exercises compute their volume from your current logged bodyweight, so set volume reflects real load rather than zero.
- Isolated user profiles with one-tap switching.
- Offline-first local SQLite persistence with JSON backup and restore (plans, history, preferences).
- Automated GitHub release update checks on Android and Windows.
The web client exposes all assistant tools over the Model Context Protocol (Streamable HTTP). MCP-compatible agents connect to https://<host>/mcp. With the default launch settings (dotnet run --project Physiquinator.Web), the local endpoint is http://localhost:5149/mcp.
All 15 tools, including get_workout_plans, create_workout_plan, log_bodyweight_entry, and get_workout_history_stats, publish standard JSON schemas. Destructive actions (delete_workout_plan, delete_bodyweight_entry) prompt for user confirmation using the protocol's input_required flow.
Configuration (appsettings.json or environment variables):
"Mcp": {
"ApiKey": "", // Required in production: /mcp rejects requests without a matching X-Api-Key or Authorization header
"CorsOrigins": "" // Comma-separated allowed origins for browser-based clients
}Physiquinator.Web is the same Blazor UI as a server-rendered web app, plus the MCP endpoint at /mcp. Every account gets its own SQLite database with cookie auth and PBKDF2 password hashes. While the page is open, the server mirrors databases to browser IndexedDB. The app ships security headers, rate limiting, and a /healthz readiness probe.
Run it locally with dotnet run --project Physiquinator.Web. The /mcp endpoint rejects requests without a matching API key, so set Mcp__ApiKey before hosting publicly. Optionally set AUTH_DEMO_USERNAME and AUTH_DEMO_PASSWORD for a demo login. Serve the app behind an HTTPS reverse proxy that sets X-Forwarded-* headers.
Physiquinator.Wasm compiles the same Blazor UI to WebAssembly so it runs entirely in the browser with zero server. There are no accounts and no cold starts. Data lives in real SQLite executing in WebAssembly, with e_sqlite3 linked via native dependencies, restored from browser Cache Storage on boot and written back by a 20-second autosave plus pagehide hooks. The browser is the source of truth. JSON backup and restore remain the cross-device bridge.
Run it locally with dotnet run --project Physiquinator.Wasm (requires the wasm-tools workload). Publish a static bundle with dotnet publish -c Release, then serve the wwwroot output from any static file server or deploy .github/workflows/deploy-web.yml, which uploads it to Cloudflare Pages via wrangler. Notes:
- Data is per-browser. Clearing site data deletes your history, so use Settings export regularly.
- AI providers must send CORS headers for direct browser calls. Ollama needs
OLLAMA_ORIGINSconfigured. - The MCP endpoint is not part of this host. Use
Physiquinator.Webfor agent access. tools/web-e2e/wasm-persist-e2e.mjsverifies persistence end to end.
Five projects sharing a common domain model and service layer:
| Project | Purpose |
|---|---|
Physiquinator.Core |
Domain model, SQLite repositories, and business logic (workouts, history, stats, AI assistant, backup, updates) |
Physiquinator.UI |
Blazor Hybrid UI (Razor class library): pages, components, and theming |
Physiquinator |
.NET MAUI host for Android, iOS, macOS, and Windows |
Physiquinator.Web |
ASP.NET Core web host: browser-rendered UI and MCP endpoint |
Physiquinator.Wasm |
Blazor WebAssembly host: fully local static build |
Physiquinator.Tests |
xUnit test suite covering repositories, services, and the MCP surface |
Key design points:
- Shared Blazor Hybrid UI: The same Razor component tree renders natively via WebView2 in MAUI and on the web.
- Platform abstractions: Notifications, vibration, file transfer, and update installation are abstracted behind interfaces with native, no-op, and test-double implementations.
- Unified service registration:
AddPhysiquinatorServices()registers core dependencies across both hosts (singletons on MAUI, scoped per Blazor circuit on Web). - Background rest timer: On Android, the rest timer runs within a foreground service with a draggable overlay and exact alarms.
- .NET 11 with .NET MAUI (Android, iOS, macOS, Windows) and Blazor Hybrid
- MudBlazor Material Design components and Markdig for AI response rendering
- SQLite via sqlite-net-pcl - local, offline-first storage
- OpenAI-compatible client with SSE streaming and tool-call loops and ModelContextProtocol.AspNetCore MCP server
- Plugin.LocalNotification, MAUI Essentials, and Android foreground services
- GitHub Actions workflows for CI, SonarCloud analysis, and signed releases, plus Playwright for E2E tests and screenshot generation
git clone https://github.com/kadato/Physiquinator.git
cd Physiquinator
dotnet restore
# Run on Windows
dotnet build -t:Run -f net11.0-windows10.0.19041.0
# Run on Android (device/emulator)
dotnet build -t:Run -f net11.0-android
# Run the web client (includes the MCP server on /mcp)
dotnet run --project Physiquinator.Web
# Run the tests
dotnet test Physiquinator.Tests/Physiquinator.Tests.csproj
# Enable format checks and commit linting (one time)
./install-hooks.shRequires the .NET 11 SDK (pinned in global.json) and the MAUI workload.
To build the Android APK without installing an Android SDK, see DOCKER.md. To regenerate the screenshots in docs/ on any OS, build Physiquinator.Web once and run node tools/screenshot-generator/screenshot-web.js.
- xUnit tests covering repositories, workout, session, and history services, stats, formatting, the AI tool registry, and the MCP surface
- CI on every push and PR: restore, build, test, and
dotnet formatverification (.github/workflows/ci.yml), including a Release publish of the WebAssembly host - SonarCloud analysis with coverage (
.github/workflows/sonarcloud.yml) - Tag-based releases (
v*): a signed Android APK and Windows package are published automatically (.github/workflows/release.yml) - Web deploy:
v*tags and manual runs publish the WebAssembly build to Cloudflare Pages (.github/workflows/deploy-web.yml) - Web E2E: Playwright suite in
tools/web-e2ecovering registration, seeded plans, and the IndexedDB sync roundtrip. Start the web host on port 8080, then run the tests:To test a host on another port, setdotnet run --project Physiquinator.Web --urls http://localhost:8080 cd tools/web-e2e npm test
PLAYWRIGHT_BASE_URL.