A personal running coach that turns your Garmin data into daily briefings, graded workout report cards, and an adaptive training plan — driven entirely through MCP.
Drift inferred · capture-to-capture
No drift recorded — single capability capture; advisories appear once its surface changes.
transport stdio · streamable-http · http counts 0 tools · 0 res
· 0 prompts
permission surface via code analysis
No tools enumerated yet for this server.
prompt-surface
shipped agent-instruction files + hidden-content / dangerous-code findings —
quoted from the analyzed source
analyzed commit da31349 · analyzer v33 · 7h ago
skills & prompt files 1
- agent-rules natejswenson-local-fitness-da31349/CLAUDE.md
danger signals1
- committed secret Google OAuth client secret natejswenson-local-fitness-da31349/tests/test_plan_calendar_settings.py :29
GOCSPX…(27 chars, redacted)
evidence-backed
findings quoted directly from the published source artifact — not inferred
code files: 124
filesystem 44
- fs (weak) natejswenson-local-fitness-da31349/scripts/ab_brief.py :171
with open(args.mock) as fh: - fs (weak) natejswenson-local-fitness-da31349/scripts/calibrate_report_card.py :67
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/scripts/capture_baseline.py :46
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/scripts/eval_fixtures.py :28
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/scripts/perf_fixture.py :31
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/scripts/score_profiles.py :26
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/scripts/score_prompt.py :27
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/scripts/shadow_run.py :42
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/scripts/warm_report_cards.py :52
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/branding.py :27
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/brief_planner.py :22
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/briefing.py :19
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/briefs.py :22
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/calendar_sync.py :24
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/card_store.py :49
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/coach.py :24
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/journal.py :26
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/ledger.py :30
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/memory.py :19
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/plan_coach.py :31
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/tools.py :18
import shutil - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/visuals.py :30
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/agent/workout_coach.py :33
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/cli.py :22
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/db.py :17
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/ingest/backfill.py :40
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/ingest/daily.py :15
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/notes.py :27
from pathlib import Path - fs natejswenson-local-fitness-da31349/src/local_fitness/plans.py :16
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/conftest.py :16
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/evals/report_cards.py :46
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/test_brief_planner.py :15
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/test_calibrate_report_card.py :23
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/test_cli.py :16
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/test_cli_brief_email.py :20
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/test_coach.py :6
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/test_docs_drift.py :22
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/test_interpret.py :292
import pathlib - fs (weak) natejswenson-local-fitness-da31349/tests/test_mcp_server.py :14
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/test_prompts.py :5
import pathlib - fs (weak) natejswenson-local-fitness-da31349/tests/test_report_card.py :2988
import pathlib - fs (weak) natejswenson-local-fitness-da31349/tests/test_smoke.py :5
from pathlib import Path - fs (weak) natejswenson-local-fitness-da31349/tests/test_tools.py :14
import shutil - fs (weak) natejswenson-local-fitness-da31349/tests/test_visuals.py :43
with pdfplumber.open(io.BytesIO(pdf_bytes)) as doc:
shell / exec 6
- shell natejswenson-local-fitness-da31349/src/local_fitness/agent/opencode_model.py :17
import subprocess - shell natejswenson-local-fitness-da31349/src/local_fitness/agent/tools.py :20
import subprocess - shell natejswenson-local-fitness-da31349/src/local_fitness/cli.py :19
import subprocess - shell (weak) natejswenson-local-fitness-da31349/tests/test_opencode_model.py :16
import subprocess - shell (weak) natejswenson-local-fitness-da31349/tests/test_report_card.py :2557
import subprocess - shell (weak) natejswenson-local-fitness-da31349/tests/test_tools.py :16
import subprocess
network 6
- net natejswenson-local-fitness-da31349/src/local_fitness/agent/gcal.py :33
from urllib.parse import quote - net natejswenson-local-fitness-da31349/src/local_fitness/agent/local_model.py :10
(``urllib.request``) since neither ``httpx`` nor ``ollama`` is currently a - net natejswenson-local-fitness-da31349/src/local_fitness/cli.py :452
import urllib.parse - net (weak) natejswenson-local-fitness-da31349/tests/test_gcal.py :317
assert source.count("import requests") == 1 - net (weak) natejswenson-local-fitness-da31349/tests/test_local_model.py :3
No real network calls: urllib.request.urlopen is mocked. Focus is the - net (weak) natejswenson-local-fitness-da31349/tests/test_security.py :12
import httpx
secrets 4
- secrets natejswenson-local-fitness-da31349/src/local_fitness/cli.py :502
client_secret = os.environ.get("LOCAL_FITNESS_GCAL_CLIENT_SECRET", "").strip() - secrets natejswenson-local-fitness-da31349/src/local_fitness/ingest/auth.py :13
import getpass - secrets natejswenson-local-fitness-da31349/src/local_fitness/web/server.py :39
API_TOKEN = os.environ.get("LOCAL_FITNESS_API_TOKEN") or None - secrets (weak) natejswenson-local-fitness-da31349/tests/test_auth.py :13
import keyring
database 24
- db (weak) natejswenson-local-fitness-da31349/scripts/calibrate_report_card.py :63
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/agent/card_store.py :47
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/agent/coach.py :21
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/agent/journal.py :24
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/agent/ledger.py :28
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/agent/memory.py :18
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/agent/report_card.py :103
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/agent/tools.py :19
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/config.py :17
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/db.py :12
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/ingest/details.py :23
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/plans.py :13
import sqlite3 - db natejswenson-local-fitness-da31349/src/local_fitness/web/mcp_server.py :19
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_calibrate_report_card.py :21
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_db.py :194
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_hr_trace.py :10
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_interpret.py :295
for banned in ("import sqlite3", "from .. import", "from . import", - db (weak) natejswenson-local-fitness-da31349/tests/test_journal.py :4
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_plan_tools.py :875
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_plans.py :517
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_plans_db.py :150
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_security.py :10
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_smoke.py :4
import sqlite3 - db (weak) natejswenson-local-fitness-da31349/tests/test_tools.py :15
import sqlite3
declared dependencies 13
- claude-agent-sdk@>=0.1.68
- click@>=8.3.3
- fastapi@>=0.136.1
- fitparse@>=1.2.0
- garminconnect@>=0.3.5
- keyring@>=25.7.0
- markdown@>=3.10.2
- matplotlib@>=3.11.0
- python-dotenv@>=1.0.1
- requests@>=2.32.0
- rich@>=15.0.0
- uvicorn@>=0.46.0
- weasyprint@>=69.0
transport_posture 1
- binds all interfaces (0.0.0.0) natejswenson-local-fitness-da31349/tests/test_security.py :268
srv.serve(host="0.0.0.0", port=18999)